【false sharing】伪共享

引言

当代计算机存储体系,基本构成磁盘、内存、cpu高速缓存、cpu寄存器。

以cpu为例:

​ 现代cpu都是多核cpu,cpu一般有3级缓存。其中二级缓存是专用的,多核处理器中,每个内核独享L2 cache;三级缓存则是高层级的缓存,它被所有内核共享。

什么是伪共享

​ 伪共享主要发生在L2 cache。内核独享L2,为了保证不同内核L2数据的一致性,缓存机制会强制刷新已更改的L2。

​ 一个数据被修改就会触发L2同步,这很直观,看起来也毫无破绽。不过,考虑到内存块设计,事情就不会这么简单了。内存通常被划分为2的4、8、16等次方KB 或者 MB大小(小批量的读写,可以兼顾吞吐量和实时性)。

​ 一旦触发L2同步,那么整个内存块上的数据都会被强制重载。那些即使不曾发生变化的数据,也会被重载。

案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
struct foo {
int x;
int y;
};

static struct foo f;

/* The two following functions are running concurrently: */

int sum_a(void)
{
int s = 0;
for (int i = 0; i < 1000000; ++i)
s += f.x;
return s;
}

void inc_b(void)
{
for (int i = 0; i < 1000000; ++i)
++f.y;
}

(不同内核,同时执行上面方法)

这里x,y会出现在L2同一内存块,x值一直不变,但是由于y发生改变。sum_a必须不断的从主内存加载x。

ps:

java代码中,一般只关注对并发修改字段的处理(加锁)。就算写到这里,我也还没想好怎么应用到自己的代码中去。if you can do, plealse mail to me. Ths!

ps2:

神奇的解决方法 — 内存块补全。x放入内存后,塞点无用数据,是内存块不能再加载y。

参考: