1.问题
CPU的Cache以Line为单位与内存交换数据。对于代码中位置相近的变量,比如:
long a;
long b;
其在Cache中可能被放置在同一个Line中。在两颗CPU频繁访问不同变量的情况下,容易出现因为同一Line内容频繁做状态同步导致的性能下降问题,这个问题就称之为Cache伪共享问题。
举个例子,比如上面的两个变量。如果CPU0上的线程频繁使用变量a,CPU1上的线程频繁使用变量b,那么根军缓存一致性协议MESI,这个Line在不同CPU的Cache上进行同步的代价是很高的。
2. 分析
还是以第一部分的例子来分析。
初始状态,包含变量a和b的Line只存在于CPU0的缓存中。CPU0上的Line状态为E。
CPU1要对变量b写入,该Line被读入CPU1的Cache中。这时CPU0和CPU1该Line的状态都是S。因为CPU1要写入,写入后CPU1上的Line变为M,而CPU0上该Line同步更新状态为I。
之后CPU0要对变量a写入,CPU1上该Line内容会先写入内存,然后更新为I。CPU0对a写入后,CPU0上该Line的状态变为M。
再之后两颗CPU上频繁的对自己需要的变量进行访问和写入,该Line的状态会来回在M和I之间摆动。背后的代价是CPU之间频繁的发送总线读写消息进而使得程序运行效果变差。
3. 解决
如果开发面临类似的问题,主要是将两个变量之间“隔开”距离,使得它们不会被缓存在同一Line中。
可以使用编译器的align标签。如果是结构体,也可以在结构体size不到一个Line size(一般是32或64字节)的时候在结构体中添加一些字段,让两个结构体“隔开”。
4. 工具
Perf c2c命令可以检查这类情况。笔者开发中用的比较少,感兴趣的读者可以自行查找相关信息,网上相关资料非常多。