仓库源文

.. Kenneth Lee 版权所有 2020

:Authors: Kenneth Lee :Version: 1.0

设计的减熵原理


看过很多设计文档,总觉得写了很多,但怎么看觉得怎么不对。我过去尝试把原因归垢于 缺乏逻辑链,缺乏目的,缺乏完整性,等等。经过这些总结后,我最近发现这背后似乎都 可以总结为对整个设计进行熵减控制的需要。

从信息上说,我们觉得一个系统是可控的,要求是它对我们呈现的逻辑简单,比如我们描 述下面这个结构:

    .. figure:: _static/减熵1.jpg

控制的一方就很简单,被控制一方(内部)无论有多复杂,但呈现给控制一方的逻辑是平 坦的,可以重复的,在第一个位置上是什么样的,在第二个位置上也是一样的,这种东西 好控制。类比一下:一个音量键,按一下声音大10%,按第二下,声音再大10%,……如此类 推,这个东西就“好控制”。控制一方包含的信息熵很低。但如果你的音量键是按第一下增 加10%,第二下增加20%,第三下降低3%,第四下静音,第五下关机……这个就缺乏可控性了— —它仍具有“确切”的反应,也制造了约束,也是“设计”,但这种“设计”,我们不认为它是“ 设计”,因为它对控制一方面制造了信息约束,不是控制方在控制被控制方,而反过来了。

这就好比我们经常说的:为了自己的欲望去花钱,我们是金钱的主人。因为有钱所以去花 钱,我们就成了金钱的奴隶了。对于后一种情形,是钱在控制你的行为,不是你在控制钱 的行为。

我们调用一个库,为了调用这个库写的代码比自己直接实现库的功能的代码还多,那么就 不是我们在调用一个库,而是那个库在调用你(的行为)。

控制接口是否平坦,首先受限于控制一方对控制的要求有多高。还是上面这个音量键的例 子,我们控制的时候还希望有音量过高控制,我们在超过80%音量的时候,要求每次声音增 长变成3%,这个接口的熵就变大了:

    .. figure:: _static/减熵2.jpg

    我们用一个折线位置表示一个不同与之前规律的一个“变化”,这幅图的折线比之
    前的直线,多了一个“折”,就需要多一个信息才能表达,这时我们就认为这个接
    口上的熵增加了。

简单理解,就是描述上面这条折线需要引入的参数与比描述前面一条直线,需要的描述参 数多了。每个参数都是控制一方的信息输出,这个熵就增加了。

但这个没有办法,因为这是控制一方期望的。控制一方对这一点有了要求。这是我们进行 逻辑设计的底线。所以,所谓不为天下先的策略,就是找到这个真正的底线,并且努力向 这个底线去靠,而不是为了描述的方便加入自己的限制。

正如在这里(:doc:../道德经直译/鱼不可脱于渊——从神秘司谈起 )讨论过的:你要让 人痛苦,你的核心诉求是这个“Crucio”,而不是“鞭打他”,后者已经用一个“具象”取代了 原始需求了。这会导致你实现需求的时候只剩下比如“找鞭子”这条路,不知道还有其他选 择(比如“找刀子”)可以用。

但所有高层抽象都要落下来,我们选定某种方法去实现控制方的这个“期望”,就必须受到 那个方法的约束,所以,你的期望是上面这条折线,但我们选择某个方案的时候,会引入 额外的约束,这个接口会变成这样:

    .. figure:: _static/减熵3.jpg

在这个图中,我们用黑线表示控制方的“原始诉求”,红线表示我们实现后达成的效果。

红线是我们实现的效果,它不完全和期望一致,但大部分时候它是和期望一致的,这时我 们勉强是可控制的。大部分设计,或者架构设计,就是这种情形。比如我们提供一个操作 系统,完美的诉求是一旦发现有安全漏洞,就进行修补。但这个现实中做不到,因为发现 漏洞,到修复,需要工程时间,修复以后能部署到每个用户的工程环境中,这也需要时间 。我们不一定能能无缝地解决这个问题。但我们有一个设计:我们有一个固定的升级周期 ,在什么时间之内发现的漏洞,我们用一切办法保证在三周内的一个升级版本中解决它…… 这个设计对于控制一方仍是“可控的”(比自己做成本低),这样的“设计”,就确实是在设 计,因为你满足了某个控制上的目的,或者说利益。

而很多设计新手,沉迷于自己的技术有多Cool,彻底离开了用户可控这个希望,把设计文 档写成这样:

    .. figure:: _static/减熵4.jpg

简单说就是你控制方想要什么管我屁事,你们都是来欣赏我的美好身段的。

所以,这种设计文档看着它洋洋洒洒写了很多东西,好像什么都有点关系,其实对构成一 个控制毫无关系。设计文档的目的是为了清晰把控制目标给出来,然后不断丈量实现和目 标之间的差距,而某些人所谓的“设计”,只是在自我欣赏自己无限的熵增,这就没有设计 的成分在里面了。

你做个硬件,告诉我发这个消息会有这个回应,发那个消息会有那个回应,这对我来说我 鬼知道我要不要给你发那个消息啊?我想知道的是:你是不是彻底兼容,PCIe4和SAS3接口 ,这样我拿一个标准SAS驱动就可以把你当磁盘用。我需要的是这个“承诺”,你写一百条一 千条消息给我,这是我用你还是你来玩我啊?

所以,丢开了控制目标的设计,等于没有设计,不进行熵减的设计,就是增加用户代价的 胡作非为,根本就没有设计。

实际上,进行熵减的设计,工作量比“描述自己”的设计工作量要大得多。写一百条消息的 格式,也是不用动脑的,想加就加了。要求这些消息全部符合PCIe和SAS标准,这个设计就 难了,至少你得把这两个标准里更多的消息都看一遍吧?要对比你的实现是否符合这些消 息吧?还要躲过所有你搞不定的地方吧?要让一个操作系统可以自由升级,你得知道你有 多少个分支,每个分支的生命周期有多长,承诺支持哪些硬件吧?这才是设计的难度。你 有什么就说你有什么,不表现为任何可控制的接口,这个东西不就是混沌么,混沌有什么 好“设计”的,不设计就是混沌啊。

这就好像《道德经》里面的“得一”,我们解决问题一切行为的目的,都是为了熵减,让那 个事情无名,不需要讨论,“无事”。吃饱了,只会想着去哪里玩,谁还想到哪里搞钱下馆 子的事情?不考虑“得一”,就不是在解决问题,而是在制造复杂度,把不是问题变成问题 了。这样的“设计”,就不是设计了。

补充1

前面这个PCIe和SAS标准的问题,还可以深挖一下。我们用PCIe的标准文本为例,其实PCIe 的文本的信息熵一点都不低,就算不考虑各种非标扩展,它包括了:

  1. 总线的topo和协议分层

  2. 跨版本时的兼容性要求定义

  3. 不同层的基本协议语义,以及物理层的电气特性要求

  4. 独立的电源管理要求以及对每层的要求

  5. 中断和错误处理方法

  6. 设备间的同步方法,包括锁机制,原子操作等

  7. 真实设备和虚拟设备的关系,以及如何为虚拟设备维持真实设备保证过的的语义

  8. 设备发现的方法,包括物理设备和虚拟设备的发现方法

  9. 复位和热插拔,包括物理设备和虚拟设备的发现方法

  10. 功耗控制,包括设备的,总线的,插槽的

  11. 链路速度控制

  12. PASID控制,内存缺页管理

  13. 扩展功能管理

  14. 等等等等

看起来这个也很复杂,似乎并没有构成一个简化过的控制接口,但它仍为使用者提供了一 个熵减的结果:只要你用这个,不会出现死锁,性能不高,功耗太多,能用这个不能用这 个. ..这些问题。这一切,都在于PCIe的设计上也保证了这个熵减的原则。它有很多详细 的接口定义,但这些定义都“对齐”到一些目的的。比如它可以用于支撑一定带宽的通讯, 它的状态机是经过推演的,它的层间关系,是经过分隔的(比如不会因为你在会话层修改 为ICCX协议就需要替换链路层),当这个“标准”做了熵减管理。符合这个标准这件事,本 身就变成一种“熵减”了。

但你只学会写两句功耗设计,写两句链路控制,然后跟我说“我也有啊,你要我写什么?” ,我告诉你写什么,我自己就完成这个设计了,你只能听一句,写一句,你做什么设计啊 ?

附录1:一个接口增熵的例子

每条指令执行一毫秒,一百条执行100毫秒,这理解起来最简单。但我们还是加上了流水线 ,于是,一条指令执行一毫秒,100条可能只需要25毫秒。那能否认为一条指令实际上只需 要0.25毫秒呢?在大量的指令的时候几乎可以这么认为,但测量单独一条指令的时候它还 是1毫秒。如果流水线可以很稳定地理解为一个叠加的行为:

.. figure:: _static/熵增例子——流水线.svg

这个熵也不高,但实际上指令又有各种依赖关系,实际是这样的:

.. figure:: _static/熵增例子——流水线2.svg

这个要理解它的熵就更高了。如果再加上预测执行,内存Cache,总线原子性行为等考量, 这个熵增就更高了。从理解和“得到结论”(有一个名)的角度,我们不希望这个接口如此 复杂,但从性能的角度,我们又必须在接口上呈现这个熵,所以,到头来,这就是个平衡 的工作。