3.241. 限制的可移动性

本文是这个文档的一个补充:限制管理

想到要讨论这个问题,是因为最近和人讨论一个设计,发现很多问题,特别是对架构师,或者对软件工程师来说是显而易见的问题,对部分设计师,特别是芯片设计师来说,居然是“很难理解”的。这让我体会到,上面那个表述,可能抽象得太高了,我们还需要讨论一些最基本的原理。

有芯片设计的同事常常这样跟我说:“计算10M的数据,比计算100M的数据快,这不是显而易见的事情吗?”,“不能一次输入超过计算芯片的最大Buffer的数据,这不也是显而易见的事情吗?”……但同时他们又常常这样跟我说:“数据多大是个黑盒,你们做软件的不用知道,交给我们就好了”,“缓冲区多大是个黑盒,你们做软件的不用知道,交给我们就好了”……

这些想法,和软件人员,特别是高层软件设计人员的理解,是完全在另一个世界里的。正如前面那个文档说提到的,对软件人员来说,逻辑链的根,是建在“限制”上的,如果我们不知道真正的限制者是谁,我们就无法处理“变化”。

我举个例子,假设有下面这样一个数据流:

../_images/%E6%95%B0%E6%8D%AE%E6%B5%81%E4%BE%8B%E5%AD%90.jpg

我们经过A-B-C-D四个软件模块的内部限制到达输出,最后能成功到达输出的数据(也许经过了变换,这个和我们主题无关,先忽略),对软件来说,A、B、C、D都是CPU上运行的,都是可以改变的东西,你说我把i3的限制前移到A模块上,有什么困难吗?——除了代码升级本身,根本没有。

所以,软件设计师,特别是软件构架师。我们其实很不在乎“名份”,我们在乎实际的结果。你开发B模块的人要生要死说i3多重要,如何如何有必要,我把它前移或者后移到其他模块去,完全看有没有需要——多大点事啊?

这个事情,推广到全系统的构架师,某个逻辑是要放到软件一方,还是放到硬件一方——这多大点的事啊?你以为你做硬件,这个东西就“硬”了?只要你的对手做出一个更能被接受的方案(无论是因为性能还是成本还是TTM),这种东西说变就变了。金玉满堂,莫之能守——“量”不能保证你的安全的。“势”才能保证你的安全。“势”是竞争力和改变的难度。

所以,系统构架需要有限制管理,比如上面这个图中i5的限制,完全就可以不考虑(但它内部可能仍有这个限制),所以我把它放在后面,它就可以少很多保护,默认认为i5不会发生就可以了,这样整个系统的效率才会高。如果前置模块接受我内部有Cache大小的限制,在提供数据的时候就已经考虑到我的Cache要素了,我的模块的性能就可能高,但你要充大头怪,说你是黑盒,前置模块给了你数据,你性能又高不起来,你说前置模块太蠢,不会“用”你这个模块?你是觉得你是白莲花呢?还是觉得你是覆巢之下的完卵?

所以,“处理10M比处理100M快”,这是谁的“限制”?我一个用户,我就想把数据从一个节点发到另一个节点去,你中间有很多障碍,但那是你这个通道的障碍,不是我用户引入的障碍,你100M发得比10M慢,这可不是我用户(前置模块)给你引入的限制,这是你自己引入的限制。你不能认为这个事情“顺理成章”。如果你的系统是无论多大的包,都是打包成1G的Burst统一发送的,100M和10M的速度就是一样的,谁告诉你这是“顺理成章”的事情啊?

除非我的用户要求,发完10M以后,才能发第二个10M,这才是用户引入的限制。因为无论换谁来做这个通道,它都改变不了这个障碍。

了解障碍是软件架构师非常重要的工作。因为如果我不能摸到你的“核心限制”,我就没有办法决定用力的轻重,我辛苦苦苦说服用户接受你的Buffer Size是10M,用户所有软件的切分都以这个为中心,以此建立了1000KLoc的代码。然后你跟我说你升级为100M?用户会让你去死!

这是整个架构设计的核心,简单看上面这个逻辑,我们可以说,Buffer Size是个变量,可以基于它是变量来进行设计,但所有的“优化”都来自固定的东西,你“变量”越多,开发成本就越多,开发成本上去了,竞争力又下去了。如果和Buffer Size相关的代码涉及的代码只有100行,把这个东西当做“变量”来做,这就成了浪费了。

所以架构设计是经验为基础的艺术,如果它能被你简单抽象出Pattern,它就不是架构了,那些是工程。所以你想学架构设计的Pattern?觉得你学两本Pattern,UML的书就搞懂架构设计?想太多了。

所以,架构师和你讨论设计,必然是要摸你的真实限制是什么的。你不要动不动告诉我你是个黑盒,你如果真是个黑盒,我很高兴,因为这样我就没有什么好考虑的,但你一次次不靠谱,事后告诉我(或者周边模块)给你的数据其实没有让你达到最优,那时我就无能为力了。你也不要跟我说你对周围的模块没有办法,是他们要这样的接口的,因为他们也是一样说你的。你们这些互相指责都不是真实的限制,只是你们人为制造出来的。我们用真正的环境限制来限制自己,不用人心和自己所处的位置来限制自己,否则你走不长。你要摸清你的限制,就先要求上级输入给你“完美”的数据来做你输入,完全按你的要求来,让你的性能达到最优,然后我们再来看为什么上一级给不了你这个输入,这样我们就摸到我们真实的障碍是什么了。人家都还没有说不行呢,你就说人家能给你的输入就是这样的,说到底是你懒得想而已。你以为你解释得通这个问题就不存在了?竞争对手分分钟教你做人。

其实,这些观点虽然看起来是架构师的观点,但实际上是模块设计师同样必须考量的,因为你也不想老修改你的模块呐。

上面的概念说起来复杂,如果你只是做模块设计(或者做硬件接口设计),我其实就期望一点,你建立概念,建立在你真正的硬件约束上。比如,你跟我说,“虚拟机给硬件寄存器写入XXX”,这个就很可能是我无法接受的。“虚拟机”这个概念,我认为根本就不关你硬件的事情,你怎么知道我软件侧是怎么定义“虚拟机”这个概念的?你怎么知道我是虚拟机里面给你写的XXX?你一旦用了“虚拟机”这样的表达,就产生了一个对我虚拟机设计产生了一个限制。我不反对有这样的限制,问题,这个限制得真的是你的限制,要不就是竞争对手也得这样干,要不就是你加入这个限制后,你的Die Size变小了,性能变高了,我都行。但很多时候,其实你根本就是图说得爽脆,给我引入一个限制,却不能带来你的竞争优势,这一个小动作,就把我们的竞争力给毁了。

还有那些动不动就给帮我考虑好怎么设计线程,然后怎么调用runtime,然后写到你的调度队列中的硬件设计师——我怎么放我的Runtime,放不放这个Runtime,在不在一个进程之内,这些都和你无关,你就是个外行,除非我不这样干,你就不能工作,否则轻易不要跳过来,我才是这个领域的专家,你根本就不懂,你不为这个设计负责,却去为这个设计增加限制,这个限制不是给我的,这个限制是给整个系统的呀。没有经验的人给有经验的人设置限制,这怎么可能达成系统的最优呢(注:这里的“我”不是说我本人,只是描述的方便。也不是说不能讨论别人的设计,而是说,守着自己的限制,就用来建自己这个模块的逻辑,不要随意去给别人增加限制,这样才能让专业的人做那个专业的事情)

看完本文的表述,不知道各位读者对下面这个文档的DFD方法是否会有更深的理解了?