3.312. 关于语义的辨识

今天和人讨论了一个关于“内存语义”的问题,发现有些基本的,对于“语义”的,理解,没有对齐,我这里建一个模型来描述这个问题。

所谓语义,是我们说一句话和我们对这句话的预期的一个对应关系。这又是一个所谓“道可道”的问题。我们在这里交流,本身就依靠语言,说一句话是语言,对这句话的预期也是语言。哪个语言在描述说话,哪个语言在描述语义呢?

所以,作为初步理解,我们先这样定义:语言是总结,语义是对总结的进一步解释。

比如我们说“内存语义”,就包含一组语言和对这组语言的深入解释。比如这是一种定义:

  • Store:把指定数据保存到指定的内存地址中。

  • Load:从指定的内存地址中获得数据。

这个定义背后其实有很多子定义去支撑的,比如这里隐含了内存的编址空间的语义,内存访问者的语义,内存序的语义,字长的语义等等。我们可以一路解释下去,直到我们达成某种程度的共识,在那些共识上,我们天然有互相认可的语义(所谓“Must Be Passed Over In Silence”)。

即使我们在基本的定义上已经有了“Passed Over In Silence”的共识了,这个事情真正麻烦的问题在于,这个期望的边界到哪里。语言通讯是双方的,所以这个期望被粗糙理解的时候通常就是是通讯双方的,但从效率来说,双方的期望不是一致的,所以,语义到底是谁的期望?

就内存这个语义来说,粗糙理解是Store的时候把数据保存到内存中,而Load的时候把当初保存过的在这个地址上的数据读出来。但其实内存使用者大部分时候不关心数据有没有保存在内存上,他关心的是“我曾经纯进去过的数据,是不是可以原封不断读回来”,至于数据有没有进入内存,他不关心。

所以,实际上,现在的内存系统在做Store的时候,不会保证一定真的把数据保存到内存中,而只是保证你写进去过的,一定能同样读出来。所以Store和Load的语义其实不是把数据保存到内存上,而是保证从某个地址Store进去的数据,一定能被原封不动地从同一个地址上读回来。

所以这个语义只是从内存访问者一方面感受到的语义,而不是内存实现方的语义,我们只是让内存实现方去逼近内存访问者的期望。

从这个角度说,语义有主从之别,一组语义,常常是为其中一方的观察服务的,观察一方是这个语义的主方,另一方(或者多方)满足这个观察上的期望,是这个语义的从方

这并不是这个问题的全部,语义只是主方的观察期望,但这个范围其实是动态的,比如,我们不关心性能的时候,我们不在乎从方使用了Cache这个概念去解决问题,我们看不见Cache,但当主方关心:

  1. 性能:什么时候数据是就近取的,什么时候数据必须真正到内存中取?

  2. 多观察者的数据传播:在本地Cache中的数据,什么时候,按什么顺序传播到其他CPU上?

这些时候,语义仍是观察者的期望,但范围被扩大了。这说明,语义是会被升级的,当我们关心的问题多了,我们对它的期望就是会变多的。你可以说,“这不是原来的语义了”,没有问题。关键在于,你还是把它称为“内存语义”。所以,我们必须意识到,同一个名字的语义,是有版本和分支的区别在其中的。

总结一下,当我们讨论“语义”的时候,我们在讨论一些总结包含了哪些子信息,这些子信息的作为一个整体,它们具有两个关键属性:

  1. 通讯双方的定义和主从关系的定义

  2. 版本和分支