仓库源文

.. Kenneth Lee 版权所有 2023

:Authors: Kenneth Lee :Version: 0.2 :Date: 2023-06-23 :Status: Released

目标约束和实施约束


这个两个概念我不知道有没有现有的学科定义过了,我先把它们表达出来,以便支撑更多 的架构问题的讨论。

我们做一个软件,都是为了满足一个目标,这个目标,约束了一个范围。

举个例子,“我要高兴”,这是一个目标,这个目标限定了一个范围,它对“我高兴”这个世 界状态的可能性是正向的,对我不高兴这个可能性是负向的。

但在让这个条件成立的举措上,我们没法直接控制它,我们没法主动去做“我要高兴”这个 动作,我们只能做“我要去看相声”,“我要去学习(寄望下次考试碾压同桌使自己高兴)”, “我要下馆子”这些动作。这些动作和“我要高兴”这个约束(注:条件就是约束,都是决定 了一个选择范围中的一个收缩了的空间),在改变世界状态上,是完全不同的,只是它们 有相关性,所以我们才采取了这些措施(约束)。

所以,从设计的角度来说,约束和约束,是不同的。我把它们区分为目标约束和实施约束。 目标约束是你无法直接达成的约束,你能实施的是实施约束。对于一个复杂的设计,实际 上我们是制造了一组实施约束,得到一组中间目标约束,然后用这组中间的目标约束,用 作下一组因果的实施约束,最终获得我们最后的目标约束。

所以,如果我们要开发一个排序算法,我们不能直接设计排序算法,我们设计的是分配内 存,读入参数,比较参数,移动指针……等等这些实施约束,期望最终得到我们的目标约束。

这其实不是一个非常严格的概念,因为在人脑总是把直接相关的东西看作是自己可以直接 实施的条件。比如开车往右拐,这不是人可以直接实施的约束,因为人直接可以实施的不 过是踩油门,打方向而已。但我们还是认为我们可以实施“开车往右拐”。所以,这就是个 所有讨论人形成共识的一个度的问题。无论如何吧,在大部分场景上,我们是可以找到这 个度的。因为如果我们如果觉得某个实施约束不能实施,大不了就针对这一个约束再分解 成几个可实施约束和目标约束而已。

分清楚这两个概念的分别有利于我们理解逻辑闭包。逻辑闭包的本质是用实施约束去推理 目标约束,或者用已经被推理出来的中间目标约束,当作实施约束,用于另一个逻辑闭包 的推理。比如,给定一个有id的列表,可以得到这个列表的最大值。前者是一个实施或者 中间目标约束,而后者是目标约束(包括中间目标约束)。这个相关性在算法推理出来的 时候,不那么直接,但我们推理过后,设计上我们就可以换用它们了。两者具有直接的相 关性。

在实际的设计中,我经常看到有些人的设计,看着总让人难受,你甚至不能说它对还是不 对,这通常是因为这种逻辑X包是半封闭的,实施约束和目标约束不能肯定具有这样的相 关性,或者这种相关性非常弱,这样的设计,你问:“它哪里不对?”。这是很难回答的, 它哪里都不对。

这就好比一个排序算法,写得非常复杂,先创建了一个二叉树,然后对每个值做Hash,然 后三个一组做sha1,然后再代入一个神经网络得到每个节点的权重……最后你说这个算法对 不对呢?其实你的逻辑思维判断不了这个东西,你要靠穷举测试去验证它,否则你就没有 办法了。很多人挑不到这种设计的破绽,觉得自己不能说这种设计是错误的。其实,如果 他这个设计要和你配合,这个设计就是错误的。因为要不你把它当作是黑盒,只凭你对人 的信任去无条件信任它,如果这个不行,他要求你能认知这个逻辑并和他配合,而你找不 到这个规律,它常常就是错误的。

我前面说的比较抽象,但其实在现实中这个问题很常见。比如我遇到过一个内存预取算法, 当时我在使用某个内存数据前调用prefetch预取数据,但性能没有提升,我就去问开发者, 这个地方为什么prefetch没有用。下面是我们的对话:

他:因为这种情况我们会自动预取。

我:为什么在后几个循环里面又没有预取呢?

他:因为这个预取用了两个变参判断是否是双重循环来判断是否需要预取。

我:但我这里就是双重循环啊

他:这个预取的算法有个队列,你这种情况队列满了

我:那多少次才会满,我能否预先清掉不用的呢?

他:这和L1Cache到L2Cache Queue的设计有关……

你看,这个整个设计就不构成逻辑闭包,不呈现完整的输入输出的规律,这个地方就是没 有设计的,只能当作一个黑盒听天由命。

再说一个例子,有人设计了一种新的页表,页表有很多种不同的配置,适配不同的VA和PA, PTE可以是64bit的也可以是128bit的,这里有很多个变量,比如每个页目录的长度,页目 录的层数,PTE的大小,等等。他写了一个Specification,写了一组“如果XXX,就会XXX”, 这样的描述,比如“如果页表翻译找不到下一级页目录,就会产生异常”,“如果 PAGECONFIG.Q寄存器为1,PTE的大小就是128bit”,……那么这些条件全部组合起来,能否 正常设计出一个MMU来呢?其实我是不知道的(我其实认为他也不知道)。这种情况,实 施约束和目标约束就没办法在闭包中建立起人脑可以接纳的关联。这种设计不用犹豫,它 就是错的。或者你也可以认为,这根本就没有设计,因为你不重新“设计”一次,你也不知 道它对不对,你设计的目的不就是在正式编码前推理一下这样行不行么?既然他的描述起 不了这个作用,那当然就不是设计了。

有效的逻辑闭包是容易被某种方法穷举的,比如对于这个页表,我们可以用一组变量,但 我们必须有一条穷举的逻辑线,比如从VA和访问属性开始输入,用参数变量完整说明最后 输出的PA和总线访问属性作为输出,这就是穷举的。比如你可以说:::

页表类型=F(VA, MMCONfIG) 页目录级别,页目录长度=K(页表类型) S1-Sn, Offset = X(页表类型,页目录级别,页目录长度) …… 然后再一路分解F,K,X的具体定义。

这样详细写下去,这个翻译过程就是穷举的,这是人脑可以控制得住。但一组分离的,没 在一根线上的逻辑,一时说说这个,一时说说那个,你根本就没法确定它是可行的。