.. Kenneth Lee 版权所有 2016-2020
:Authors: Kenneth Lee :Version: 1.0
怎么做高层设计
看过很多高层设计都做得没有方向。本文要剖析两个常见的思路误区,帮助新手找到正确 的方向。
第一种常见的错误是,把高层设计写成了科普读物。比如我们为一个平台做虚拟化支持, 有人就会写成这样:
1)什么是虚拟化,以及虚拟化在数据中心中的作用
2)硬件MMU对CPU虚拟化的支持
3)硬件IOMMU对设备虚拟化的支持
4)KVM和Xen的软件架构
这叫科普读物,不叫高层设计,甚至就不叫“设计”。“科普”写的是“某某是某某,某某怎么 样”,“设计”写的是“我选择某某”。
设计是一种选择。
很多工程师把设计写成了科普,是因为他们没有意识到代码之外的设计是必须的。所以在 没有完成代码前,你让他写高层设计,他就不知道给你写什么好。
高层设计是在更大投入前做的先导决策。是我们做复杂工作的基础。比如前面这个虚拟化 支持,我们的一种写法是这样的:
1)需求一览
2)基础虚拟化解决方案选择( 比如:KVM+libvirt)
3) 硬件虚拟化解决方案选择
3.1)网络(SR-IOV)
3.2)存储(virtio)
3.3) 加速器(VFIO-platform)
4)前置依赖特性一览
4.1)qemu支持DTS自动生成VFIO-platform配置(第三方上传中,需要push和跟踪)
4.2) VM_PINNED特性上传(特性需要由本团队自行上传):支持加速器直接使用malloc内 存
4.3) VFIO-platform在qemu中通用配置方案
4.4) 虚拟IOMMU设备主线支持方案
5)软件版本配置
5.1)主线版本:特性集1
5.2) 客户群1战地版本:特性集2
5.3)客户群2战地版本:特性集3
这才是设计。设计都是选择。
如果你不做这个设计,你的结果只有两个:
1)你会无限度地进行各种特性的验证,最终你这个特性出不来
2)你会抛弃前期的理性决策有限度地进行假设,做出一个不俱备竞争力的产品来。
所以,高层设计(特别是高到构架设计这个层次),是不可忽略的。它不是某些自以为“务 实”的家伙想的那样,是一种无关紧要的劳动。好的高层设计,功夫都在文档在外。决定选 择Xen还是KVM就是大量的调查研究。决定让IOMMU支持二层页表还是Guest中使用无IOMMU的 DMA,也是用大量的编码实验作为基础的。
这是我说的第一个误区。当我们给一个作者反馈说他把设计文档写成了科普读物,他很容 易就陷入另一个误区:他觉得他只是写得不够详细。所以,它解决这个问题的方法是加快 工作进度,延迟交付时间,先把代码编出来,然后写高层设计。
这个问题还是前面提到的问题,这其实还是相当于不做高层设计而去直接编码。出现这种 情况,我感觉有两个原因。
第一,作者没有足够的编码经验。在没有把代码写出来前,他完全无法对代码进行预判。 比如他不知道注册一个平台设备需要哪些数据,不知道device和driver什么时候bind,什 么时候unbind,它不知道为什么iommu要支持两层页表映射,不知道这种映射由guest, host还是Hypervisor执行。甚至,他无法预期他实现的一个dma_map到底要多少个参数,给 定的参数是否足够。他什么都不知道,你让它定义一个接口,他不可能做到。他需要写一 小段,编译一下,看看是不是可以工作的,然后在写一小段,再看看能否工作,说不定还 要跟踪一下流程。这种人,你让他写高层设计实在是难为他了。这种就不要想什么高层设 计了,自己骗自己,就拿着编码来做练习,等着跌上几交,教够学费了再说吧。
第二个原因是认识问题。这样的作者可能刚刚从前面说的那种学习状态中成长起来,并没 有意识到高层设计是一种设计。以需求来说,有些高层设计者在撰写高层设计中的需求的 时候,总想找一个“客户”或者其他模块的接口人来签个字,说这是他们的需求。然后才能 把需求写出来。
他们没有意识到,在高层设计中的需求也是一种设计,是设计师对产品市场反应的一种判 断。你能在手机摆到柜台前让消费者决定买不买吗?你能在他们决定买不买后才决定做不 做这个特性吗?
这是不可能的。
但我们进行设计的时候,需求是设计师的判断。用户有很多很多的祈求,但并不表示你一 定可以满足,也不表示你必须满足,你要判断你手上的筹码,可以换来多少用户满意度。 这个不像编译后的代码一样,可以运行,这个东西需要在你的脑子中“运行”,但这不表示 它不是一种像编码一样精确的设计。只是说这个设计的不包括最终代码的所有细节,它选 择定义的每个细节,选择Plan A还是Plan B,都严重影响后续的工作量。
需求之外的高层设计同样如此,比如我做一个加速器,我首先考虑我给的参数和运行模型 是否足够,我可以这样定义用户接口:::
queue *acc_request_queue(capability)
acc_queue_set_opt(queue, new_capability)
acc_request_sync(queue, request_data, fallback_function)
acc_request_async(queue, request_data)
acc_wait_async_event(queue)
这套API没有细节,但queue可以怎么定义,我能肯定我总能定义出来,我就可以留给详细 设计,但我这个定义有两个东西是不容易立即推演出来的,一个是异步线程模型,我要考 虑在有多个线程的时候,这样定义是否足够我分配线程,第二个是如果我加上的 fallback_function用来用CPU取代加速器的加速能力,我能否匹配我关注的那些要支持的 软件的编程模型。所以,这个设计就就足够我去推演在需求上是否满足多线程,多设备的 可行性。还有能否嵌入现有需要加入的程序中。
比如我定义了这个接口,有一个战术版本的人对比它的场景,他会注意到,这里每个 request,都会产生一次系统调用,而他的场景中,这样的次数就会太多,他就可能不接受 这个设计。又或者说,对照过很多个应用场景后,fallback_function的形态各异,做在这 一层没有意义。我们就可以把对应的地方改掉。但如果你一开始就定义queue这个结构的形 态,这就会造成浪费。
所以,高层设计可以和详细设计表现出来几乎一样的,但高层设计是设计师眼中的设计。 如果它只能在你完成代码后写,或者只能用来科普,你还是别写算了。
对新手来说,很多时候这里是有心障的。因为在学习阶段,他们看惯了各种设计得面面俱 到的成型的代码,而设计是产品的中间过程,构架设计更加是多年演进产品的中间过程。 面对设计过程是痛苦的,因为你不得不面对选择,并面对缺陷的这种令人失望的东西(这 也是为什么新手容易把设计文档写成科普文档过程,科普文档忽略了选择,不用面对“我做 了一个不成熟,不能面面俱到的产品”的心障),但把一个要做3年才做成的产品设计得面 面俱到,你有什么成功的机会?