.. Kenneth Lee 版权所有 2018-2020
:Authors: Kenneth Lee :Version: 1.0
怎么确定道
最近给两个新来兄弟辅导设计方法,突然对慈,俭,不为天下先有了一个直观的解释,我 把它整理在这个文档中。
这个文档是针对开发人员写的,但基础逻辑基本上是种哲学性质的推演,我尽量在前面表 达得通用一些,把程序相关的讨论放在后面,以便对不写程序的人也有一点参考价值。
之前的博文已经讨论过了,道是现实发生的逻辑路径,它具有三个特性:
多面性,我们只能获得它其中一个侧面。球进了球门,你可以关注球在门内的事实,也 可以关注是张三踢进去的,也可以关注不是门柱弹了一下它也进不去,也可以关注球是 第三运动器材厂的产品。所以,没有“关注点(妙)”,无所谓道。至少不是可以谈的道
部分道已经发生,不可改变。(已道)
部分道还未发生,这是我们关心道的核心,我们希望确定这个道,顺着这个道走,我们 就可以达成目标。(未道)
本文要讨论的问题就是确定这里第三点的策略是什么,说明为什么“慈”,“俭”,“不为天下 先”这三点是确定“道”的重要原则。
我们现在有一个位置,我们心中有一个目标,这是两个点,如果我们用一个平面坐标轴来 做比喻,它是这样的:
.. figure:: _static/确定道1.png
如果我们不考虑任何障碍,最好的路径是在这两个点之间连一条直线,这就是最优的“道” ,它包含的概念最少,描述这个“道”,只需要起点和终点的坐标:
.. figure:: _static/确定道2.jpg
这里只有两个名。如果用现实来类比,好比我要从小吃店里去巷尾上公共厕所,店员告诉 我“道”怎么走:
| 你从这里顺着巷子往前走,一直走到头,看见门口挂着“公共厕所”牌子的就是了
中间经过一间杂货店和一个半人马雕像这种事,不在你,也不在他的关注点中,这都是“无 ”(视之不见,名曰夷;听之不闻,名曰希;搏之不得,名曰微。)。
这两个东西,在整个表述,或者你的思考中,都看不见。你急着上厕所,什么杂货铺打折 ,半人马巧夺天工这种事,都和你无关,这就是叫五色令人目盲,五音令人耳聋。我们聚 焦在一个妙(interesting)上,就不会关心其他角度了。
什么时候你会关心它们呢?——你遇到障碍的时候。
巷子中间施工,挖了一个大坑。你要绕过它:
.. figure:: _static/确定道3.jpg
你道(未道)可能就变成这样了:
| 从这里顺着巷子往前走,遇到一个杂货铺的时候,进去,
| 从后门出来,左拐,继续走,遇到一个半人马雕像的时候,
| 左拐,一直走到头,看见门口挂着“公共厕所”牌子的就是了
这里你就引入了两个新的“名”。我们优化“未道”这个逻辑链,第一个基本原则:不增加不 影响“道”方向的名。这就叫不为天下先。在道德经中,这个原则是针对领导者来说的,所 以,有一个额外的含义:不要引入额外的需求。但还是那句话,我们现在聚焦”设计“这个 主题,其他东西到我们需要的时候再去推广。
好了,基本原则我们有了,现在核心的问题是,在我们没有走过去的时候,我们怎么知道 那里有没有障碍。这就是”知不知“这个问题的策略了:我们需要去调查,确定哪里有障碍 。但调查本身呢,也是一种投入,直接去做也是投入,调查也是投入,如何平衡两者的关 系?最好当然是“做本身就是一种调查”,调查完了,该做的也已经做了。但很多时候我们 做不到,所以,我们尽量聚焦到和“道”转弯有关的核心问题上。也就是说,在我现在要走 的那条路最相关的信息,是我优先调查的位置,调查到什么程度,是架构师,领导者的本 身对问题的认识问题。但至少,在基础方法逻辑上,我们得聚焦在“道”上。所以,我们的 基础方法是:把所有的“知”先标记出来,把“不知”也标记出来:
.. figure:: _static/确定道4.jpg
其中红色的是知其可行,透明度表示它的置信度,黑色表示知其不可行,白色表
示未知
有一副这样的图,我们要判断下一步是调查还是执行,就是一件很自然的事情了。这样我 们很容易理解什么叫“慈”和“俭”了。不硬冲就是慈;不绕路,就是俭:
.. figure:: _static/确定道5.jpg
所以,慈故能勇,能避开的都已经避开了,剩下的就没有什么可以纠结的,硬冲就是了。 围城必缺;抗兵相加,哀者胜之。没得选的,总是比有得选的更勇。
绝学无忧,对于求道者(求目标达成者),没空跟你讨论和道无关的逻辑。所有的逻辑都 是聚焦到“选择”上面的,没得选择的时候就是“事善能”,那个时候拼的是“勇”,更不跟你 谈逻辑。所以我们不求头头是道,我们求“最优选择”。
好了,哲学说完,我们谈谈怎么做设计的问题了。设计是代码的“道”,最终的代码是我们 的目标,但如果我们前期不对我们实现整个代码的“道”有一个正确的认识,我们选择的可 能就不是最优路径,只会碰得头破血流。比如你选择一个不支持多线程的库来作为你的编 码器,然后就开始基于这个库创建会话,分配内存,进入这样的细节,你的资源消耗大半 的时候,你发现性能不够,就好像走了遥远的路了,目标在一座万丈高山的后面,近在咫 尺,你也只能看着了。
所以,对于设计,我第一反对的是你没有设计,没有设计就表示我必须把一切都寄望在最 后。特别是那种三个月,半年才交货的代码,没有设计我都害怕得要命,要知道,做这种 判断,可不是在你交货的时候开始的,而是在你应用的时候才发生的。比如你做一个SAS控 制器,某些新插盘进来的时候需要重新扫描整个阵列,结果libsas根本不支持,你想改, 但libsas现在连维护者都没有了,什么都改不进去,你自己的驱动从一开始就没打算弄…… 这种问题,验收都发现不了,都是到了用户的场合才知道的,你前期不推演,到应用的时 候你跟我说改,改条毛噢。(严格来说,这个没有需求支撑推演也不一定推演得出来,但 就这个问题来说,因为是个状态机问题,进行逻辑推演是可以捕获的)
所以,设计没有你想的那么复杂,你有5个需求,每个需求大概怎么做?你打算放几个.c, 几个.py,里面还有几个.go?他们怎么发生关联,从而那个功能可以被实现?你有多少个 版本要合?这些版本编译成多少个二进制?这些二进制部署在Docker/LCX中,VM中,还是 Host Kernel中?这些都是说说而已(不是代码,也没有用代码校验过),但我们都有经验 ,知道这样弄,成功的可能性有多大,这就是很简单的事情。但你不想用这个心,在过程 中怕批评,怕接受别人的意见,不断把你的描述说的头头是道,无懈可击(所以很多人才 会到代码写完才肯交设计),你最后就到不了你想达到的那个道。这种心态,弄来弄去, 最终一辈子都活在“解释”中,永远都体会不了“成功”的快感。
始终有一张图景,我们就不断有办法判断我们下一步是“写一部分程序”,还是“做一个代码 验证”,还是“调查一下某个未知的情形”。这我们就会离成功近很多。而不是,上来就开始 写代码,遇到障碍就来给理由,文档写得技巧十足,道理满满,无懈可击,遇到质疑就解 释,背后抱一个“没有功劳也有苦劳”的心理支撑。对于这种,我老实说,我的策略是“不代 司杀者杀”,我该帮已经帮了,你自己要掉下去,这我可帮不了。
没有这种图景,我们想慈也慈不了,想俭也俭不了。但另一方面,我们建这个图景,是为 了能改变路径,避开不知和障碍。但其出弥远,其知弥少。我们不是为建这个图景而建这 个图景的。所以很多设计文档,缺乏对这个图景的聚焦,那也是没有用的。比如,很多人 会拷贝完整的代码作为设计文档,把写验证代码做的数据结构,原封不动地拷贝上来,显 得专业,复杂,但这种复杂度,对你提前发现设计逻辑中的问题毫无帮助。我问你“为什么 要在设计中强调device_group有一个lock和一个rb_tree”呢?你告诉我“因为访问 device_group需要上锁才能访问啊,device是个稀疏阵啊。”,我靠,这句话真有道理,我 简直无言以对。你说的都是对的,但和我们正在讨论的大的设计,分多少个模块,主要功 能是否可以实现,关联度有多高呢?
所以,设计这种东西,不同层面有不同的取舍,取决于我们最后要做的决策是什么,架构 也可以有非常细致的设计的。我的博客里经常有人问我为什么文字有相当多的定语重句, 老天,我这也叫多,你们要知道我每天对着,或者自己写着,是什么样的鬼话。我拉这个 典型的给你看看:
| A read or write operation is single-copy atomic only if it
| meets the following conditions:
|
| 1. For a single-copy atomic store, if the store overlaps
| another single-copy atomic store, then all of the writes
| from one of the stores are inserted into the Coherence
| order of each overlapping byte before any of the writes
| of the other store are inserted into the Coherence orders
| of the overlapping bytes.
|
| 2. If a single-copy atomic load overlaps a single-copy
| atomic store and for any of the overlapping bytes the
| load returns the data written by the write inserted
| into the Coherence order of that byte by the single-copy
| atomic store then the load must return data from a point
| in the Coherence order no earlier than the writes inserted
| into the Coherence order by the single-copy atomic store
| of all of the overlapping bytes.
这段描述来自ARMv8的构架定义。你看,构架也可以有构架的“细节”的,但每个这样的细节 ,都是支持构架这一级的逻辑链的。你看看人家这个构架文档怎么说的:
| The ARM architecture described in this Architecture
| Reference Manual defines the behavior of an abstract
| machine,referred to as a processing element, often
| abbreviated to PE. Implementations compliant with
| the ARM architecturemust conform to the described
| behavior of the processing element. It is not intended
| to describe how to build animplementation of the PE,
| nor to limit the scope of such implementations beyond
| the defined behaviors.
注意最后那个强调,它是精准来源于缩小范围,聚焦要解决的范围的,而不是什么都有的 。
我的设计大部分都不能公开,这里有一个难得是公开的,可以作为参考:
:doc:`Progress and confusion of the IOMMU name space`
我这里喋喋不休地描述iommu_group的原始定位,vfio_group的设计意图,竭尽所能要描述 出现各个势力在这个问题上的预期图景。这些观点,我不认为他们对,也不认为他们错, 但我需要把它们摆出来,都是为了形成前面说的这个图景。只有有了这样的图形,只有我 对他们有正确的认识了,我才能避开所有的障碍点,找到一个满足我的要求,最容易切进 去的点。
在我写本文的时候,那个文档已经是第5个版本了,每个版本都是有一个设计者提出不同意 见后进行根本上的修改然后才重新整理出来的。
写这种东西,说到底就是聚焦(决策逻辑链),确切(无二义性),反而正确性是放在第 二位的,因为本来我就是无法彻底正确所以才写出来取得共识,你要面子,避开这种有可 能的错误,不如不写。另一方面,你们看看那个描述的逻辑的复杂度,我能把脑子控制住 所有的要点就不错了,我还有空关心你的情商,文笔之类的东西?我首先要保证的是我的“ 正确认识”,然后才是别人的正确理解,最后才是什么情商,文笔这些字节。加花是主干稳 了以后的事,主干都没有稳就讨论什么“这样描述更好”,“这个概念不是这样定义的”之类 的,基本上都是腐儒之见,你们不过是在讨论自己的面子和名声,根本就没有关心那件事 本身啊。
要满足我的特性,最终我肯定是要修改别人的模块的,比如现在那个上面的决策是要修改 type1驱动对mdev的特殊处理的。但所有的路径都考虑过了,能躲开的都躲开了,那就是个 所有力量聚焦要突破的地方,那有什么好想的?我们就不会再犹豫了,就是一个冲字。这 就是慈所以能勇。
其实说来说去,这些东西可能很多工程师不是不知道,可能还是有“怕露怯”的心态。而且 这种心态是根植在骨子里的,如果不主动去露怯,把心思全部放在求道上,根本自己都不 知道(自己在求名),可能永远都走不出来。
怕漏怯的另一面就是装逼成狂,明明已经弄清楚的判断,非要向外拉逻辑:关于这个 iommu_group你们不知道吧?这个可是当初我和某某某谈笑风生决定的……这里面还有一个故 事……要不就是知乎式的“这种大路货的知识也敢拿出来显,你懂XXX,YYYY,ZZZZZ吗?”
天之道,高者抑之,下者举之。要准,不是要“牛”,名字露出来就已经“不牛”了。
最终的结果始终就是我给你谈道,你来给我谈情商,谈面子,谈站在我这一边。对这种, 我还是这句话,我无能为力,我不代司杀者杀。但你如果认为“常”没有“司杀者杀”,我没 有什么可说的。
(对了,我的表述和任何真实事件无关,就是想讨论一个逻辑,所有的情绪表达,不过是 为了行文生动。还是那句话,如果你不关心这个道,而关心扯是扯非,我是帮不了你的)