CS巴别塔(1)

面试宝典3:计算机体系结构

Posted in Uncategorized by Kenny Yuan on 2010/10/31

(这段声明还是加上比较好:此《宝典》系列虽然名为“宝典”,但写的都是一些“不知道就别出来混”的基础知识。这一篇也仍然还是基础中的基础,而并非是什么高级货色——任何一个合格的在校本科生,学过计算机体系结构这门课的,都要懂得多得多——在我看来唯一可被理解的例外是那些在当年上学时,被劣质教材和老师害了的人。本文末尾有一本推荐的书籍,建议此类人士进补)

Human-Architecture

Human-Architecture

让我们来假设,有那么一天,作为软件工程师的你,面对这样一道Computer Architecture面试题:

程序在运行时,计算机内部到底会发生什么样的事情?

在继续阅读之前,请你先回想一下相关知识,并估算一下所涉及知识点的数量:

A. <10个;

B. 10-50个;

C. 50-100个;

D. >100个

请选择: ______。

……

以下,是我的回答(非标准答案,供参考):

首先是要搞清楚这台机器具有什么类型的ISA。ISA有四种:Stack,Accumulator,Register-Memory,Load-Store。现代的处理器都是Reg-Mem或者Load-Store的,或者说:有一些ISA,表面上是Reg-Mem这样的一只被汇编程序员所喜爱的喜羊羊,实际上它也是一头Load-Store的灰太狼(例如x86就是在CPU内部进行翻译的)。Stack类型的ISA似乎已经没有流行的硬件了,但是在许多Virtual ISA——也就是在各种高级语言虚拟机(HLL VM)中,却非常地流行。究其根本原因,应该是为了在各种不同的机器上移植的方便(因为这种结构对寄存器没有特别的要求)。

如果是Load-Store的ISA,那么可以肯定它是一个RISC处理器,那么它必有pipeline。例如经典的MIPS的5段流水线(ARM是3)。前面刚刚说过,不仅仅是RISC处理器,连x86也是把翻译出来的RISC操作码发射到超标量流水线中。这些辛辛苦苦翻译出来的操作码会用一个cache来缓存,名为trace cache。如果是RISC指令,因为其格式固定,还可以对其进行固定位置解码(fixed-field decoding)。在pipeline中,也有一些对软件透明,所以不太出名的部件,比如pipeline register,用来在各个stage之间传递数据。

pipeline不是随时都在满负荷工作的,时不时地会有一些停顿(stall),比如:因为结构冒险(structural hazard)引起的停顿。结构冒险是流水线冒险的一种,举例说明:如果register file的并行读写操作太多,port已经用光了,那么就会有一个停顿插入流水线;如果指令和数据都是存在同一个存储器中(冯·诺依曼结构),在同一时刻内又有WB(写回数据)和IF(取指令)的冲突,那么就需要插入一个停顿(也叫pipeline bubble)。哈佛结构不存在这种限制,而现代的冯·诺依曼处理器将Data Cache和Instruction Cache分开,也没有这种限制了(是的,不光是CISC和RISC,哈佛和诺依曼的界限也不是那么清晰了)。

(上面说到了存储器,我们先记下,一会接着再说它,否则就失去连续性了)

引起pipeline stall的原因,除了structural hazard,还有可能是control hazard控制冒险,比如各种转移(branch)。有了转移,就需要对转移进行预测,以便提高效率,否则流水线的损失太大太大。一般常用的转移预测(branch prediction)有这样几种:2-bit历史预测,多级相关预测(比如2级2bit 4KiB)。要进行这些预测,就需要额外的存储,一般CPU内建有BTB也就是branch target buffer。有些CPU中有多个这样的BTB,用于不同的预测,比如有的专门用来预测局部的转移(local branch prediction)。

有些转移是无条件的,比如call和jmp(goto),在现代处理器中对这种无条件的转移基本可做到不需耗费额外时钟周期。还有一种特殊类型的转移预测:return address prediction。我记得有个数据在C++或者JAVA这种高级语言中RET会占15%左右的转移总量,有了这个数据,这种类型的预测立即就有了意义:) 同理,对loop进行专门的loop prediction也是很有意义的。

除了前面说的两种,还有一种流水线冒险:数据冒险(data hazard)。数据冒险有三种,RAW(Read After Write),WAW(Write After Write)和WAR(Write After Read)。如果对R和W进行排列组合的话,其实还有RAR。但是我喜欢7z,不喜欢RAR,所以我说RAR不存在数据冒险(于是它就没有数据冒险)。

对于WAW和WAR,可以通过寄存器重命名(register renaming)来解决,也就是说,汇编大佬们注意了:CPU并不诚实,它欺骗了你,还一笑而过……嘿嘿……另一个可能会让汇编大佬伤心的事实是:处理器会乱序执行(out of order execution)多数指令(需要ROB — Re-Order Buffer的帮助),而且指令还是多发射(issue)的,这时候都没有先后关系了……前面讲的这些工作和一位几十年前在IBM工作,名叫Tomasulo的老爷爷有关(都已经过世两年了)。他用自己的姓来命名算法,所以大家虽然记不住他叫Robert,但还能知道他姓Tomasulo。

关于数据冒险,还有一种类型是RAW(Read After Write)。简单的解决方法是插入pipeline stall,等着数据写完再读,同时引来了pipeline的低效。更快的方法是在流水线内部的pipeline register直接传递数据,叫作直通或者短路。但即使加上了这种技术,有时候还是需要停顿,因为有时候从逻辑上来说,是必须要等的。

上面说的都是在ILP(Instruction Level Parallelism)级别的东西,其中也提到了内存,所以下面开始说一说内存相关的。

说到内存,就离不开Cache。Cache是一个伟大的发明,但一开始它却是为TLB(Translation Look-aside Buffer)服务的,因为“内存分页”的概念虽然好,但是多次间接地访问内存,搞得效率实再是太低下,于是有CPU设计者,在忍了再忍、忍无可忍的情况下,揭竿而起,给页表加上了cache——性能问题立即就得到了解决。

TLB和普通的cache一样,也会有hit和miss,现代CPU中的TLB在locality原则的保证下,甚至能够达到99%以上的TLB hit,但是那1%的TLB miss的penalty却非常地expensive(两个数量级以上),以至于无法忽略。据说TLB可以放在所有cache之前,或者在多级cache之间,或者在所有cache之后,分别对应:所有cache物理编址,L1 Cache虚拟编址+L2 Cache物理编址,以及所有Cache均为虚拟编码的三种情况,但实际上cache似乎都是物理编址的。理论上来讲,越大的Cache越需要用物理编址,否则一旦碰上进程切换……哼哼……

说到TLB就不得不说页表(本来它就是页表的cache)。页表有两种方式:多级页表和反向页表。32位地址模式下x86是三级每页4KiB,如果启用了PAE则是2MiB(这里多说一句,PAE不是Bank Switching,因为PAE扩展的那几位是参与VA->PA计算的)。AMD64位模式下为4级页表结构48Bit地址线。页表中对每个项(PTE)都至少有Present和Modified两个标志。P标志指示此页是否真在物理内存中,M标志指示此页是否在上次载入后被更改过。这两个标志是用来做虚拟内存的磁盘交换的,如果一个页被交换到磁盘上,它的P位就是False,访问它就会引发page fault中断,于是OS中断服务程序开始介入,假如不是非法访问的话,就要去从磁盘装载页面数据到物理内存,一般这时会硬盘狂响,界面冻结,用户开始捶打键盘……如果一个页由磁盘装入了内存,但一直还没有被更改过,那么M位就是False,也就是说如果在将来这个页要被换出的话,可以不必将它的内容写到磁盘。Linux内核中的pte_present()和pte_dirty()就是用来查询这两个标志的。除了这两个标志,还会有一些其它的,比如是否可更改等,用来做内存保护。

另外,Linux的程序装载,也是依靠page fault的,所谓的装载器其实并没有将程序内容从磁盘向内存复制(名不符实的东西遍地都有,是吧?),而是靠mmap机制映射之,随后在用到时,自然会有page fault出现,于是程序自然就被一点一点装载好了(这种感觉有点像刚刚写好了一个原语操作,然后就立即用它去发威了)。

TLB和Cache是紧密结合工作的,一个VA的VPO(Virtual Page Offset)会直接送到Cache中做匹配,VPN(Virtual Page Number)则送到TLB作查找,二者并行进行,如果都是hit,PN又对得上,那么这就是我们最想要的情形了。反之就要准备好至少两个数量级以上的penalty。在有些CPU中,TLB也有data TLB/instruction TLB,以及L1 TLB/L2 TLB之分。更多类型和更大的TLB cache力求减少miss,用以保证更好的VA->PA性能。

说完了TLB回过头来再说Cache。一般的Cache都是Set Associative的,因为Full Associative会慢,而且代价高,Direct Map又太不灵活(有一个经验定律是:2-way Set Associative,与双倍size的direct map cache相比,其cache miss rate持平)。因为Set Associativity的特性决定了有时候cache line必须被换出(此时称为victim,可以用LRU或FIFO来决定谁是victim),所以这里面就有一个优化:放置一个victim cache,专门用来保存上次被换出,但是马上又要被换入的那个line(我还看过有paper说用空闲的cache来做virtual victim cache)。OS中也有许多利用Cache的Set Associativity的特性,比如Page Coloring, Slab Coloring, Hot-n-Cold Allocator(另:设想Buddy System和非Full Associative的Cache一起使用,会有什么问题?)

从类型上来说,Cache分instruction cache和data cache两种,分别用来cache指令和数据。前面在说pipeline的时候曾经说过,如果在流水线中同时有“将运算结果写回”和“取指令”两个操作,存储器是无法接受这样的任务的。但区分了Instruction和Data两种类型后,就不存在此问题了,因为两个cache当然可以同时读+写。这种划分的另一个好处是,指令和数据的各自有自己的locality(包括时间和空间局部性),而且很少有交集(那个会写LISP的不要笑!严肃点!我们这儿侃大山呢!),所以区别对待它们,能带来性能上的提升。

一般CPU中cache至少有两级:L1和L2,现在开始流行三级cache了。越小的cache越快(基本和寄存器相当),越大的cache越慢,但无论如何也比内存快(最慢的也比内存快一个数量级左右)。分级的cache就带来一个问题:L2缓存中是不是有全部的L1缓存的内容?L3缓存中是不是有全部的L1&L2缓存的内容?这个决策不是一成不变的,有三种情况:如果大的cache一定含有小cache的内容,那么就算是strictly inclusive的;如果一定没有,就是exclusive的;如果基本上有(但有时候可能没有),就是inclusive的(注意和strictly inclusive的区别)。如果是inclusive cache,那么显而易见这里面就有一个更新的问题:更新L1 cache的时候,什么时候再去更新L2 cache中的对应内容?

层次当然是越多越好

层次当然是越多越好

另一个cache问题是在多核心/多CPU系统中出现的(SMP和NUMA均有),一般称为cache coherence问题。每个核心/CPU的L1 Cache一定是独享的,那么,如果相同Physical Address的内容被装到了不同的Core/CPU的L1 Data Cache中,然后这些Core/CPU又要去更新它们,就会出现问题。为了解决这个问题,在硬件中提供 了directory方式或者监听的方式,其中监听方式很简单:监听别的cache中的写入,如果是相同的PA,那么可以将其标为invalidated状态然后重装入,或者将监听到的写入动作同样执行一遍。

关于Cache,还有一个初看无关,但是细细考虑后,发现很相关的问题:DMA。经典的DMA讲解中,把“数据装入了内存、从而能够被直接使用”当成一个很终极的目标,但是现在我们还有更快的Cache,倘若每次用到DMA数据就需要从主存中取——这倒是没什么别的问题,就是太慢了——用惯了这么快的cache,早就把我们都惯坏了。所以又有一种办法被提了出来:对DMA的数据进行cache pre-fetching。当然这种pre-fetching不能太过份,否则cache会被这些东西给灌满,而真正需要用的热数据都被换出了——这就是所谓的cache polluting。cache polluting其实就是过量的cache warming-up以至于伤害了性能。

(曾经在某讨论组见过一个狂人,说整个组的人都不懂什么叫set associative cache,只有他自己一个人懂得……会不会我这篇blog侵犯到这位狂人独有的知识产权了呢?嘻嘻……)

说完了Cache这回终于能够说到内存了。内存的访问速度很慢,比CPU要慢两个数量级以上——这也正是Cache的意义所在。Cache也是内存,只不过是SRAM,特点是速度快;而内存是DRAM,特点是集成度高(其实计算机体系结构的许多问题都是由速度和容量的tradeoff产生的)。和双稳态的SRAM不同,DRAM每隔一段时间(典型数据:几十毫秒)就需要刷新数据以保证不丢失。过去的DRAM都是异步时钟的,直到SDRAM的时候才变成同步的(S就是synchronous)。DRAM颗粒是通过RAS和CAS信号来选取地址的(先用RAS锁出一行到行缓冲,然后靠CAS挑出需要的),每个内存颗粒可以一次选出8bit,然后这样8个8bit,并列起来构成了64位宽的数据(话说SDRAM和DDR都是64位宽的,更老的EDO和FPRAM什么的就让它们往事随风都随风吧——还不如关注一下双通道和三通道呢!)。有些内存模组上面有9片内存颗粒,这就是ECC内存了,一般是给服务器用的(Google的人曾发paper说,统计一下他们用ECC发现的内存错误,发现比想像中的可要频繁多了——对几万台服务器进行了长达两年半的跟踪记录,发现每年会发生22,696个可纠正的错误)。这里我不打算详细讲解校验原理,只做一个这样的计算:不带额外的颗粒时是8×8=64位,用它编码可以表达2^64种状态。现在是8×9=72位,也就是说可以用2^72种状态来表达2^64种编码,也就是说每256种编码中,就只有1种是对的,其它的255种排列方式都是错的……(嗯,Richard Hamming老先生要是看了我这样来解说,不知道会不会气得跳出来,然后再演讲一个Me and My Regret)

内存的访问有alignment一说,也就是说取32位数的时候,最好低5位地址为0,64位的则要求低6位地址为0。如果不是对齐的话,就需要两次存储器访问才能把完整的数给拼出来(注意这时候不考虑Cache的问题,完全是在说内存本身)。

在内存和Cache之间,同样有数据同步的问题, 为解决此问题,在Cache中也设计有dirty标志位:如果Cache要被淘汰掉了,并且dirty位为True,那么就需要把cache中的数据写回到memory中。当然,也有可能是用Write Through方式更新memory(不过,即使是Write Through,也是可以利用buffer来后台批量更新memory的,而不像传统教科书上讲的那样地naive)

关于内存访问,我想说的最后一件事是:程序中的内存访问操作,其顺序并不是按照程序规定的那样,而是经过了Memory Re-Ordering的。这种机制是乱序执行的一种,CPU中的MOB(Memory Ordering Buffer)就是来做这件事情的。

说完内存,就可以开始说外存了。如果不把穿孔纸带/卡算在内的话,外存是从磁芯磁鼓磁带一类的东西开始的,中间还有过8英寸、5英寸和3英寸软盘(后来又出现过LS-120/Zip/MO等软盘替代品,但它们都被U盘给干掉了)。现在的主流外存是硬盘,不久的未来可能会是SSD的天下。倒是磁带这东西还没有全死,因为容量实再是巨大(最新的大约是一卷500TiB);或者严格说来,软盘也没有全死:那些闻名世界的中国制造小工厂,常常使用老旧设备,那些设备上的计算机只能吃软盘:) 硬盘是从PIO方式发展过来的,后来才出现了DMA方式,后来又制定了DMA66/DMA100/DMA133等标准。有个经验规律是说,带宽的增长是延时的增长的两倍速度,但对于硬盘来说,这增长的带宽只是通讯线上的理论速度,硬盘本身的连续传输速率还是受机械机构的限制,不可能太快;延时方面则是基本没变。最新的硬盘外圈的连续速率可以有100+MB/Sec,但内圈速度至少减半。之所以有这样的速率差别,是因为硬盘是恒角速度旋转的(光盘倒是可以恒线速度),在相同时间内,外圈转过的距离当然比内圈要长,于是就可以多安排扇区。传统教科书上认为每个磁道上的扇区数都是固定的,但硬盘厂商早就打破了这一限制:他们在硬盘内部的固件里把逻辑扇区映射到物理扇区。这样的映射还带来另一个好处——试想,现在的硬盘是10年前的1000倍容量,如果工艺的改变没有1000倍(这种好事不用去想的!),那么坏扇区出现的故障率就大大增高了(比如100倍以上)。所以每个硬盘在正常的存储区之外,还有一些额外的保留区域备用,一旦出现了坏扇区,硬盘会把出错的逻辑扇区映射到保留区域内,这样就可以保证硬盘继续正常使用,只不过受到一点性能的损失(原来还有可能是连续的,现在是一定不连续了)。希捷硬盘的三黄问题,其中有一黄就是这个“重映射扇区”的计数。

1GB Hard Disk vs. 1GB Flash Card

1GB Hard Disk vs. 1GB Flash Card

如果硬盘中的碎片不特别严重,那么空间局部性原理也是起作用的(时间局部性原理多数会起作用,但考虑到OS有disk cache,可以稍微放松一下要求)。所以硬盘也安装有Cache,用来缓存读和写,在测试硬盘时候出现的最高的峰值速率都是Cache的功劳(硬盘用的Cache就是普通的DRAM颗粒)。

最早的硬盘是并行通讯。很多年前,并行通讯是比串行通讯快的,因为线多,可以同时传送一个字节或者更多。但后来发现,如果提高时钟频率,线多了会导致串扰变大(比如DMA33只要求40根的排线,而DMA66的线需要80根,其中就是添加了40根地线,每根地线和信号线并排),这就限制了时钟频率的提升,最终影响到通讯速率(还限制了通讯线的长度)。设想一下:如果串行通讯的时钟速率能有并行通讯的8倍以上(大约吧,因为还有其它开销),那么8位并行口就可以被打败了。实际上,用了低电压差分信号的串行线可以相当地快:比如PCI Express(过去叫3GIO),其时钟速率是2.5GHz,而PCI总线是33MHz或者66MHz(服务器上用的PCI-X最高有133MHz的)。当然,多组LVDS信号也是可以并列的(注意这仍不是并行通讯),这样就有了另一个优势:可以根据带宽需要来灵活安排。硬盘也在这几年完成了从并行到串行的进化,SATA中的S就是Serial的缩写。串行的最后一个优势是成本,如果提升速率带来的结果是价格翻倍,制造商们就不可能答应,因为一般来说消费者不会为这个买单。

Parallel->Serial->PS2->USB, And It Works

Parallel->Serial->PS2->USB, And It Works

硬盘的另一个进化,或者说革命,是SSD。SSD有两个巨大优势:一是访问延时(或相应的IOPS)。硬盘的典型寻道是10ms左右(硬盘直径小一些,就可以转得再快一些,寻道时间就可以再小一些,但至少也有几个ms;NCQ也可以优化寻道时间,原理是减少多余的空转),在这期间CPU能完成大约三千万条指令的运算。而优质的SSD(非中国山寨品)可以在1微秒内完成“寻道”(这是软件测试数据,实际有可能更小,因为本来SSD就没有“道”,就是做地址转换而已),至少提高了4个数量级。反过来用它折算IOPS,如果均为随机访问,大小为1Byte,那么这样的SSD至少应该有10k的IOPS;而10ms寻道的硬盘,一秒内至多能够有100个IO。SSD不仅仅是极限值估算中占优,在测试软件的IOPS实测中,也是以数量级领先于硬盘。SSD的第二个优势是速度,读取速率领先硬盘2-6倍。SSD的写入速率比SSD的读要慢百分之十几(SLC),但仍然大幅领先硬盘,而且SSD的速率在未来可能还会大幅提高,反过来看硬盘,在这10年间,盘片介质的传输速率也就是提高了两到三倍。

(往上翻了翻,发现打了一大堆字了,幸亏我有Cherry的机械键盘,打起字来一点也不累,嘿嘿……另外,如果是用嘴说的话,估计嗓子早就冒烟了……嗯,顺便在此BS一下弃blog而去录音频的tinyfool,然后咱们继续……)

说了这么半天,还有一件重要的东西一直还没提到:中断。同步/异步中断,可屏蔽/不可屏蔽中断,硬件/软件中断(软件中断由指令引发,常被用来实现系统调用),这里允许我虎头蛇尾一下吧,此处就先不展开了。

嗯,另外还有一个有趣的技术,SMT。这个技术的思路是,反正现代的CPU都是superscalar的了,程序经常无法用上这么多的运算部件,但如果把它们拆成两个传统的处理器核心的话,在可以充分ILP的时候却又吃亏了,而且需要的部件也变多了,再考虑到某些情况下流水线的长长等待停顿,设计者就盯上了近些年来开始流行的多线程技术,在CPU内实现了多线程并行——这也是SMT名字的由来:Simultaneous Multi-Threading。SMT属于线程级别的并行(TLP),它和ILP不同,它同时解决流水线垂直和水平浪费的问题,从充分利用运算部件的角度来说,SMT肯定是赚到了,但是从Cache和TLB等的角度来说,多个线程有可能互相warm up也有可能互相pollute,胜负难料。另外,从CPU设计的角度来说,支持SMT肯定会更复杂(register file需要再大一些,program counter需要两份等),坊间有传闻说Intel的Core2系列CPU就是因为没时间去做而放弃了SMT……嗯,传说而已,作为不明真相的雪亮群众,我们一定要保持情绪蛋定。

最后写点和知识无关的话:我上面写的这些体系结构方面的东西,全部都是因为组装电脑才学到的。有一本名叫CSAPP的教材(国内翻译版名为《深入理解计算机系统》),被某些人奉为神器,但在我这个装机十几年的人眼里,其中没有任何新鲜的知识,都是炒冷饭。

然后,请允许我吐一个槽:有些写程序的人,格外瞧不起会装电脑的,声称那些装机高手都是XXXX……在我看来,这有可能是他们自身知识层次的限制,外加一些很常见的biases(具体的我就不列举了,有兴趣的同学可以回家翻心理学教材),才导致他们会说出这样的话来(而且还有个逻辑错误)。

P.S. 说起基础的教材来,倒是《Computer Architecture – A Quantitative Approach》这一本,相当地不错。本着国内技术圈儿喜欢8褂教材的习惯,我在这里多介绍一些背景:作者共有两人,其中John Hennessy是RISC的大牛,斯坦福大学的校长,MIPS的发明人和公司创立者;另一位David Patterson是伯克利的教授,RISC的大牛,SPARC处理器原型的发明人……(嗯,看来我的8褂工夫不到家,说完这几句就没词儿了——这俩人获得冯诺依曼奖什么的不知道是不是应该8一下)……我个人建议:每个有志把程序写得更好的人,至少要把此书精读一遍(在上学时没有学到好教材的,该进补一下了)。其中的理由,我就不论证了——应该会有一小撮儿的software die-hard同意我的观点吧?最后,套用一句公司老大的话:

Software + Hardware = Complete!

An Architect

An Architect

感谢 @welfear 指出几处问题。

然后,自己留言给自己“辩解”一下:这个文章是在某一个周六的下午闲得无聊憋得难受就打一口气打出来的(前一个月左右曾复习了一下体系结构方面的东西,外加某些情况,不写写文章就有点憋得难受了:),最近我还没有仔细review,如果有错误肯定是在所难免,等回头对这些文字开始生疏的时候,我会再来修改订正。所以,大家千万看的时候要注意挑错,别轻信我说的,呵呵……

Advertisements
Tagged with:

资深架构设计师的30条忠告

Posted in Uncategorized by Kenny Yuan on 2010/10/18

UI really matters!
界面真TMD不能忽视!

The design should base on the problem domain, not on what you have done before.
设计是应该按照问题域来进行的,而不该根据你过去做的事情为蓝本。

It’s hard to say “I was wrong”, but if you don’t say it, you’ll have to pay for it.
说出“我是错的”是很困难的,但是如果你(在该说的时候却)不说,你会为此付出代价。

It’s not your full time job demonstrating how to write code. You can do it, but not all the time.
你的工作不是做编程示范。你可以做,但是不要全身心投入进去。

Bug fixing is also your responsibility, don’t leave it to the maintenance team.
处理BUG也是你的责任,别把它扔给维护团队就不管了,要不然你的设计会被维护团队搞得面目全非的。如果写烂代码更方便的话,维护团队一定会写出大堆的烂代码的。作为架构师,你要去控制修改代码的成本,让成本最低的那一个成为你所期望的那一个(借鉴一下经济学)。

You are not Winston Wolf. Try to eliminate problems before they surface. (good design has less problems; you’re not a firefighter)
你不是一个专门来解决麻烦的(见《低俗小说》)。试着在麻烦出现前将它们干掉。(好的设计能消除问题和麻烦;你不是消防队员)

Design is technical, try to avoid politics. Or negotiate with politics to make things still work.
设计是技术问题。不要让政治参与其中。或者适当妥协以便让事情还能运转。

If your decision is based on some limitations, always remember those limitations along with your decision.
如果你的决策是基于某些限制的,那么在记住你的决策的同时,要记住这些限制(不要直接记住结论)

If you can summarize principles from your work, write it down. Then sometimes you’re able to know you were wrong.
如果你能在工作中总结出原则,就写下来。这样以后你就能知道你曾经如此这般地犯过错(我打赌你写过的多数都是错的,除了OCP,但那不是你发明的,嘿嘿)

If there are many programmers awaiting for your design, you’re definitely dead meat. (Human Resource driven is the sin)
如果有太多的程序员在等你的设计(来开工),你就死定了。(人力资源驱动是一种原罪)

Prevent lame metaphors; software is neither art nor brick building.
别用蹩脚的比喻;软件不是艺术,也不是砖墙。

Tell the user what you can provide; don’t ask them what they REALLY WANT.
告诉用户你能提供什么;别问他们“你到底要什么”。

Quality is your responsibility too, don’t hand it over to QA. (Design in the robust way)
质量也是你的责任;别把它们交给QA。

Don’t get smart. Write it down when you feel like a genius, and attack your idea as the most dangerous enemy.
别耍小聪明。当你感觉自己像天才似地做出一个设计的时候,要把它当成最危险的敌人来对待。

Reuse is not your purpose, it’s neither necessary nor sufficient to success.
“重用”不是你的目的。它不是“成功”的充分条件,也不是必要条件。别单纯为了重用去设计,别忘了重用是为了达到什么样的目的。

A language addict will always be a slave. Try to become a sensai.
语言上瘾者永远是一个奴隶;尝试变成斯普林特老师吧!

Solutions/Tools with high price guarantee nothing on productivity.
售价很高的解决方案/工具从不保证任何生产力。

Don’t say “Microsoft did the same thing”. It proves nothing. (Google/Facebook/Twitter/etc.)
别用“微软也这么干过”来支持你自己。它什么也证明不了(google/facebook/twitter同理)

Don’t try to collect information/requirements first and decide later; welcome to the moving planet.
别尝试先收集所有的信息和需求,然后再决定;欢迎来到地球——这颗移动的行星!

There’s no universal solution; The programming language sometimes can be the one, unfortunately we have lots of languages.
没有万能的解决方案;有时候编程语言可以是一个(万能解决方案),不幸的是我们有很多种语言

Age and experience are not the right way to defend yourself in an argument.
在争论中,年龄和经历不是你证明自己的正确方式(以德服人,以理服人)

Yesterday’s workaround is tomorrow’s limitation.
昨天的Workaround就是明天的限制

Good design evolves; bad design patches.
好的设计在进化,坏的设计不停地打补丁

Design what you’re able to write code for it. If it’s too hard to you, don’t count on the others.
用你能实现的方式来设计;如果困难到你也写不出来了,那么就别指望其它人

Don’t be the captain in the engine room.
不要成为引擎室里的船长(把船开好是你的职责,而添煤则不是)。

Inspect your design in each level.
在不同的级别下仔细检视你的设计。

Understand the hardware; your system doesn’t run in air. (Hardware goes wrong too)
要懂得硬件。你的系统不是在空气中运行的(但是可以Adobe Air)(硬件不是完美的,可能会出各种错误;)

Make your design natural and comfortable. Don’t fight against programmers, and don’t let them fight against your design.
让你的设计是自然而舒服的;别跟程序员斗争,也别让他们和你的设计斗争。(参考QT的舒适与灵活,外加良好的命名和强大的功能;另参见最小惊奇原则)

It’s too late when you realize it’s slow; design for performance;
当你意识到速率太慢的时候,已经太晚了。为性能而设计。

……嗯……如果你仔细数过了这些原则的数目,会发现其实只有29条。第30条我没有写,其实原打算是这样的:

(30)这篇文章是由Kenny Yuan在1.5小时内直接敲出来的,其中英文敲了一小时左右,中文翻译了半小时左右;他写这篇文章是为了说明:滔滔不绝地谈论具有迷惑性的原则是多么的容易!而且居然还能骗到一部分人哩……

(P.S. 之所以选择从“架构”这个角度来忽悠,是受到以下这个的启发:http://97-things.near-time.net/wiki/97-things-every-software-architect-should-know-the-book)

最后祝大家动脑快乐!

(P.P.S. 此为旧文,一直在草稿里忘了发。现在编辑一下发出来,其目的,当然是想要给大家的Google Reader再添一个未读数,嘻嘻——嗯,要keep it on the down low…)

渔讯——这拨儿都是咸带鱼

Posted in Uncategorized by Kenny Yuan on 2010/10/13
Big Fish

Big Fish

一段时间以来,我在慢慢总结自己的知识结构。原来是用DOT来做图(优势是可以画graph而不是仅仅限定于tree,而且还能上版本管理),但后来因为种种原因,转到了xmind的Mind Map方式——既然用了xmind,那就可以开始分享了。虽然我估计当前的完成度只有两、三成左右(很多地方没有深入展开,有些类别也还没有添加),但本着“Getting Real”的思想,还是尽早拿出来分享吧!题目仍然是标题党的形式:Interview Bible (面试圣经),嘿嘿

地址如下(可下载):

http://www.xmind.net/share/kenny_yuan/interview-bible/

Interview Bible (collapsed)

Interview Bible (collapsed)

Tagged with: