仓库源文

.. Kenneth Lee 版权所有 2017-2020

:Authors: Kenneth Lee :Version: 1.0

基于“语义”编程


本文尝试更清楚地定义什么是“基于语义进行编程”,我在前面博文的描述中,使用“基于语 义编程”来和基于“能跑”来决定编码行为的编程行为模式进行对比。但我没有深入解释什么 才是基于语义来编程。

简单说,基于语义编程是基于每个语言元素的抽象进行逻辑组织,而不是基于当前的具体 情形进行逻辑组织。我用id_t来表示一个id,在当前情形下,我把id_t定义为int,不表示 在所有情形下id_t都是int,你要基于这是一个id来考虑程序逻辑,而不是基于“现在不就 是int吗?假定它是int有什么不行的?”当id成了int,这个不属于id的语义就会被使用( 比如我会直接用id顺便保存int),等id经过一个特定的传输接口的时候,本来可以进行截 断传输的,就不可以了。换句话说,不基于语义进行编程为程序逻辑引入了“超过需要的依 赖”,让系统的熵增加快,让程序更快变得不可维护。

我们用一个例子开始深入探讨这个主题。比如我们读一个文件的内容,有些人会这样写( 我没有校验这个代码,只表明意思):::

    int out_sz;
    char data[HEADER_SIZE];
    out_sz = read(file, data, HEADER_SIZE);
    if(out_sz != HEADER_SIZE)
      return -EIO;

这个代码,很可能是基于“能跑”的思路来写的。比如read的返回值是ssize_t,不是int, 这里的out_sz为什么要用int呢?这些人会辩解说:我写过一个例子打印过了——这两个东西 的大小是一样的。或者他们会说:我看过头文件了,ssize_t就是int……这些辩解,就意味 着你是基于“能跑”来组织程序的,不是基于语义来组织程序的。在你的平台上ssize_t是 int,不表示设计这个表述的人是这个“意思”,我们顺从设计者的意思,就有机会在广泛的 人群中形成更大的共识,从而让程序在这种共识下是都是可以移植的。

read的返回值被设计成了ssize_t而不是int,就意味着它的语义是:“一个足以放得下本平 台地址空间偏移的值”,而不再是“一个和平台字长一致的整数”。我们的上下文需要什么样 的语义,就应该用什么样的语义来构成你的代码,而不是基于“我试过”了来构造你的代码 。

我知道很多著名的公司为这种问题在升级字长,修改大小端,升级多核上付出巨大的代价 的。他们实际面对的问题也就是这种非常基本的问题,比如明明应该用void * 来表示指针 ,却用了u32,最后在数百万,数千万行的代码中,完全无法区分到底哪些是指针,哪些是 数据。或者用锁调度来实现互斥,到系统变成多核以后,所有的锁都无效。很多公司为此 付出数千万美元乃至数亿的代价来解决这种问题。

我个人对这种问题的经验是:大部分场景根本不要指望去升级代码,这些代码已经要死了 ,老老实实给他一个养老的空间,比如让它单独跑一个独立配置的进程空间内,32位程序 就给一个32位的兼容代码空间,没有考虑多核的程序就让它只有一个线程,通过多个重复 的进程来提升它的Scalability……而新代码就让它在新的地址空间中工作吧。否则这个框架 永远是你的僵梦。这种经验也表明:不注重语义的代码死得快。

基于语义进行编程,很多时候完全不体现在代码上,比如上面例子中对返回值的处理。你 说它是设计意图也有可能,你说它是“能跑即可”也有可能,这和我们的整个设计意图相关 。但如果我们遵循基于语义进行编程的原则,读出的大小和请求的大小不同,可能有如下 原因:

  1. 文件到尾了,这种情况是否在你预期之内?在的话你应该如何处理?不在呢?这样就 应该有处理流程去响应

  2. 磁盘坏了,这种情况你的程序到底应该继续还是应该整个退出?

  3. 程序在这个过程中收到信号

  4. 其他非预期的情形

基于语义写程序,这些都是你可能遇到的情形,只是你现在跑不会遇到,但你写程序也不 是为了解决你调试的时候的问题啊,你的程序可以跑在几十万个基站的铁塔上,几百万个 机顶盒上,几千万个手机上,数不清的漂流在各大河流中的Sensor上,你得保证的是在那 些情形下能工作,不是在你的计算机上能工作啊。

也许基于你的设计语义,你最后还是选择仅判断大小,但这个选择和你没有经过上面这个 设计过程的选择不一样啊。

很多工程师不愿意基于语义来编程,是因为基于语义编程,消耗时间多,但产生的代码量 少,不利于表功。我昨天下午花了整个午去解决一个问题,发现我们一个程序运行的时候 需要Tainst Linux Kernel,我看一大堆的LKML邮件,Patchset和代码,得到一个结论:这 个问题在4.14后就不存在了。所以我整个下午工作,一行代码,一个文档都没有输出,只 是输出一个结论。这个太不方便在领导面前表功了。很多程序员都不愿意这么干,对此, 我只能说说情怀:你到底是要当一个好程序员呢,还是要当码农呢?就算从功利的观点来 说,我看到收益比较好的,也都是好的程序员,而不是所谓不断在领导面前表演勤奋的所 谓码农啊。整天表演“我很惨”,却写了十多年程序,连自己工程的Makefile是怎么布置的 ,read的返回值是什么都搞不清楚,你说你混那么久,除了增强了“表演能力”,还得到了 什么呢?拜托,我们是为自己活着的,不是为领导活着的啊。