高性能多线程程序中的false sharing和CPU cache效应

高性能多线程程序中的false sharing和CPU cache效应

1. false sharing

在多核快速发展的现在,利用多线程技术提高CPU设备的利用率已经成为一种趋势。然而多核计算机体系架构和单核有了很大的变化,在多线程编程中会碰到一些意想不到的问题,比如多核中非常典型的false sharing问题。下文会非常详细的揭示false sharing产生的根源,以及何如避免来提高程序的性能。
先来了解一下典型的多核架构,每个CPU都有自己的Cache,如果一个内存中的变量在多个Cache中都有副本的话,则需要保证变量的Cache一致性:变量的值为最后一次写入的值。Intel多核架构实现Cache一致性是采用的MESI (Modified/Exclusive/Shared/Invalid) 协议。
高性能多线程程序中的false sharing和CPU cache效应_第1张图片

以上图为例,初始P1和P2都从Memory中加载变量x=1,这时每个CPU的Cache的x变量均处于Shared状态;当P1写入x=3时,P2中的变量成为无效状态,P1的为Modified状态。以后在P2读取变量x的值,由于P2的Cache的变量x是无效的,致使Cache命中失败,同时系统会用P1的值来更新P2的Cache和Memory,。
高性能多线程程序中的false sharing和CPU cache效应_第2张图片

需要注意的是,CPU Cache的结构是按CacheLine为最小单位进行读写的。在Linux可以用命令行sudo cat /proc/cpuinfo看到Cache信息。

本人的机器信息如下:Cache Size: 3072KB, Cache_alignment:64。

下面的例子就能说明false sharing产生的原因:

view plaincopy to clipboardprint?
01.double sum=0.0, sum_local[NUM_THREADS];
02.#pragma omp parallel num_threads(NUM_THREADS)
03.{
04.int me = omp_get_thread_num();
05.sum_local[me] = 0.0;
06.#pragma omp for
07.for (i = 0; i < N; i++)
08.sum_local[me] += x[i] * y[i]; //产生false sharing的代码行
09.#pragma omp atomic
10.sum += sum_local[me];
11.}

在上图中,CPU0和CPU1的sum_local数组位于同一个Cache Line中。比如某个CPU中的线程更新sum_local[]时,会使其他CPU的Cache的sum_local变成Invalid,这样其他CPU中的线程访问该变量的时候就会进行更新,导致Cache失败,这样会造成额外的内存和Cache之间的同步代价。

在实际的多线程程序中为了避免这种情况,可以采用如下几种方法:

1, 每个线程使用局部线程数据;

2, 每个线程访问的全局数据尽可能分隔开至少超过一个Cache Line。

2. CPU cache

  多核环境下CPU cache的同步一致性。

你可能感兴趣的:(高性能多线程程序中的false sharing和CPU cache效应)