仓库源文

.. Kenneth Lee 版权所有 2022

:Authors: Kenneth Lee :Version: 0.2 :Date: 2022-08-22 :Status: Released

软硬件结合设计的实践问题


原文

本文是为参加一个分享论坛而进行的逻辑准备,主题是软硬件结合设计,我准备通过本文 推一下这个问题有没有东西可以谈。

我最近十年基本上都在做软硬件结合的各种设计,在我的这些设计经验中,我并不觉得软 硬件结合和单独做一个软件的设计有什么不同(单独做一个硬件设计会如何我不知道,毕 竟我并没有硬件或者芯片的设计经验),大部分问题其实出在概念建模上。

因为我们谁都不是万能的,软件不见得懂硬件的细节上的难处,硬件不见得懂软件在细节 上的难处。这个问题和应用不一定理解OS和DB的难处,DB和OS不一定理解应用的难处是一 样的,技术在不断发展,我们原来觉得是瓶颈的地方,现在不一定是瓶颈,只要你不是深入 去钻研这个领域,你就没法知道这个领域真正的限制是什么。

特别对于现代软硬件来说,这个限制常常是逻辑工作量,这种类型类型的限制,常常不那 么明显,我们很多判断的失误,都出在这个地方。

逻辑工作量这个说法,不知道是不是每个人都有直接的感觉。比如说,有人提出一个Idea :把页表分成两层,第一层使用大页,第二层使用小页,这样平衡了两个矛盾:如果数据 在Cache中,第一层击中就可以获得数据,而查询TLB的压力很小,而真正进行也分配,在 第二层,也避免了物理内存的实际浪费。

这说起来增加的逻辑不多,但这种在整个逻辑空间中横切一刀的做法,对已经存在的逻辑 就都会有冲击。比如,磁盘Cache的算法,Fixup代码的算法,VM迁移的算法,都会被这样 的创新冲击。看起来我们只是增加了一两个逻辑,其实我们增加了无数的逻辑。比如我们 可以挑出这样一个破绽来:如果一层的Page是一个有名Page,发生缺页的时候,会要求加 载整个大页的文件内容,二级页表就必须无条件分配所有这些小页,最终就会发生过度分 配,这个性能通常就是无法接受的。

这个功能是硬件设计的人提出来的,他认为“软件只是需要访问内存,并不关心这些内存如 何分配”,所以他认为可以取代软件来进行这种实际的分配,但软件并不是这样认为的,而 是认为我知道我分配的页面的NUMA,大小,性质,速度等等所有属性的,甚至我把这个属 性直接给了应用层,这个事情,中间层(比如OS,中间件等)自己都控制不了这个属性如 何被使用,改变这种分配策略,就基本上不可行了。

这个案例让我们看到了什么?现代的软硬件,已经发展到相当成熟的地步了,每个bit,每 个gap,每个可以被利用的逻辑隔间,都被利用来做性能优化了。寄存器不是寄存器,是 rename的;线性的执行不是执行,是OoO的;物理地址不是物理地址,是Stage2地址;全局 变量不是全局变量,而是Cache中的同步效果;串行的执行序列不是串行的,而是多个序列 分时占用CPU……这些封装,都是谎言,每个谎言都要用千百个其他谎言去圆,所以,只要你 调整一下原来的说辞,你会发现原来的那些谎言,就都要露出马脚了。

而计算机,恰恰是一个“天网恢恢,疏而不漏”的系统,任何一种错误的可能性,都会在连 续的运行中变成一个“必然”。

所以这种问题如何解决呢?没有完成整个系统,没有把这个系统放在各种场合中运行,你 也无法校验这些谎言什么时候会露出马脚。

我的方法是用一个个的“逻辑闭包”(或者又叫“封闭逻辑空间”)来校验这种改变:你看, 我们有了一个现在可以使用的系统,我们认为它是“基本可靠”的,然后我们改变了它的 一个基本假设,那么,对于这个假设的每个线性相关用户,发生了什么改变?线程眼中 是否还能保持线程的直接观感?OS眼中是否保持OS调度上的直接观感?硬件眼中,是否 保持硬件严重的直接观感。

比如我们做过一个协处理器,可以相对CPU更快速完成一个计算,但这个计算本身的容忍时 间是比较长的(比如1分钟),我们在计算的过程中允许异常和中断,发生这种中断后,协 处理器的数据会临时保存在一个成为cp_state的寄存器组中……fixme:这个不影响全文逻辑, 晚点细补,先把全文逻辑写完。

在这个过程中,如果我们只是考虑硬件的行为,我很可能会漏掉很多东西,但如果我们补 充了线程,VM,内核,编译器等多个角度的视角,我们看到的东西就完全不一样了。

所以,这里的关键问题在于,我们必须用一个视角看到的问题,进行穷举,保证这个视角 没有问题了,然后叠加第二个视角,进行这个视角上的穷举,如此类推,当我们大部分高 层视角都推通了,我们再进一步在细节上进行补充,这时整个“谎言”可以被重新圆起来的 机会就变大了。

这个方法,本质上就是架构设计的基本原理。但关键在于我们现在很多的设计,特别是软 硬件设计,常常都不肯设计逻辑闭包。软件说自己不同硬件,所以不肯对硬件做假设;反 之硬件说自己不知道软件的要求,要等软件设计完了才能写设计。被迫写了设计,又常常 没有进行穷举,或者顾左右而言他,设计不形成逻辑闭包,这个设计就其不到其面说的作 用了。

一种最常见的例子是,软件没有写出代码不肯动笔写设计,硬件没有看见100%网表不肯 Release Spec。这个事情花去我大量的时间,我到处去和人强调:写出代码以后的设计 文档已经不是设计文档了,代码都出来了,设计上你早做了选择了,那个时候就算你改 设计,也不可能有时间重新编码了。

即使我们说服了设计师提前进行设计,你会发现他们总是很害怕犯错,不肯把话说死。我 们这里说把话说死。不是说怎么把细节说出来,不是要把寄存器的编码定义出来,但如果 作出的选择不是一去不能回头的,又何需要你去做”设计“呢?

比如我看过一个设计,讨论通过Cheri形式的指针,实现一个VM,如果进行严格的“设计”, 你不见得需要在乎这个指针如何编指,但你要完成这个设计,是必需要先定义你的系统中 有哪些角色,这些角色分别怎么被创建,为什么被管理的角色不能越过控制范围去操作这个 角色不能有的权力。这个封闭空间才被定义“自恰”了,然后我们才能基于这个空间去讨论 用某个角色去运行一个传统的虚拟环境这个视角的时候,所有原来说好的谎言,是否仍然 生效。

关于正面的经验问题

上面这个逻辑我写成了胶片,反馈给论坛的组织者。他不是很满意,他委婉地说:这主要 是一些反面的经验,能不能分享一些正面的经验?

这个反馈引起了我的反思:我是不是那种自己做不成事,只会在旁边挑别人的刺的小人? 为什么我的思考模型总是说什么是不对的,而不是什么是对的?

反思以后,我的结论是这样的:

第一,我不是只会在旁边挑别人刺的小人。因为我做的产品都达成目标了。如果你非要问 我是怎么成功的,我却没有什么经验可以分享,因为我不能说我做了哪件特定的事,所以 这个产品做成功了。我只能说“幸亏我没有选择做那个选择,否则现在就死得很难看了”。

成功不是因为一件事,因为成功是在所有关键问题上没有犯错误。而失败可以只因为其中 一件事。所以失败很容易,成功很困难。

但另一方面,我确实可以建模自己做成的某件事的时候,比较重要的关键坚持是什么。但 这样的描述怎么看怎么像自恋式的自吹自擂。那种东西更使用用来介绍某个具体产品的方 案,而不是用来讲“软硬件协同”设计经验。

所以,这个分享,就这么算了吧。