然而,在执行过程中,会发现,6 个线程居然跑不过 1 个线程。因为根据上面的例子知道 result[]数组中的数据在一个 Cache Line 中,所以,所有的线程都会对这个 Cache Line 进行写操作,导致所有的线程都在不断地重新同步result[] 所在的 Cache Line,所以,导致 6 个线程还跑不过一个线程的结果。这叫False Sharing。
优化也很简单,使用一个线程内的变量。
void thread_func (int id)
{
result[id] = 0;
int chunk_size = total_size / nthread + 1;
int start = id * chunk_size;
int end = min(start + chunk_size, total_size);
int c = 0; //使用临时变量,没有cache line的同步了
for ( int i = start; i < end; ++i )
{
if (test_data[i] % 2 != 0 ) ++c;
}
result[id] = c;
}
把两个程序分别在 1 到 32 个线程上跑一下,得出的结果画一张图如下所示(横轴是线程数,纵轴是完成统的时间,单位是微秒):
上图中,可以看到,灰色的曲线就是第一种方法,橙色的就是第二种(用局部变量的)方法。当只有一个线程的时候,两个方法相当,基本没有什么差别,但是在线程数增加时,会发现,第二种方法的性能提高的非常快。直到到达 6 个线程的时候,开始变得稳定(前面说过,CPU 是6核的)。
第一种方法无论加多少线程也没有办法超过第二种方法。因为第一种方法不是 CPU Cache 友好的。也就是说,第二种方法,只要 CPU 核数足够多,就可以做到线性的性能扩展,让每一个 CPU 核都跑起来,第一种则不能。
参考链接:
https://zhuanlan.zhihu.com/p/445961133
https://mp.weixin.qq.com/s/s9w--YRkyAvQi4LQcenq4g