C++ OpenMP循环阵列访问中的错误共享
我想利用OpenMP使我的任务并行 我需要将相同的量减去一个数组的所有元素,然后将结果写入另一个向量。这两个数组都动态分配了C++ OpenMP循环阵列访问中的错误共享,c++,c,performance,openmp,false-sharing,C++,C,Performance,Openmp,False Sharing,我想利用OpenMP使我的任务并行 我需要将相同的量减去一个数组的所有元素,然后将结果写入另一个向量。这两个数组都动态分配了malloc,第一个数组由文件中的值填充。每个元素的类型均为uint64\u t #pragma omp parallel for 对于(uint64_t i=0;i
malloc
,第一个数组由文件中的值填充。每个元素的类型均为uint64\u t
#pragma omp parallel for
对于(uint64_t i=0;i
其中,shift
是我要从vec
的每个元素中删除的固定值size
是vec
和new_-vec
的长度,约为200k
我在Arch Linux上使用g++-fopenmp
编译代码。我使用的是Intel Core i7-6700HQ,使用8个线程。当我使用OpenMP版本时,运行时间要高出5到6倍。当我运行OpenMP版本时,我可以看到所有的内核都在工作
我认为这可能是由于错误的共享问题造成的,但我找不到它。您应该调整迭代在线程之间的分配方式。使用
schedule(静态、块大小)
您可以这样做
尝试使用chunk_size值乘以64/sizeof(uint64_t)以避免上述错误共享:
[ cache line n ][ cache line n+1 ]
[ chuhk 0 ][ chunk 1 ][ chunk 2 ]
实现这样的目标:
[ cache line n ][ cache line n+1 ][ cache line n+2 ][...]
[ chunk 0 ][ chunk 1 ]
您还应该以与缓存线对齐的方式分配向量。这样可以确保第一个块和后续块也正确对齐
#define CACHE_LINE_SIZE sysconf(_SC_LEVEL1_DCACHE_LINESIZE)
uint64_t *vec = aligned_alloc( CACHE_LINE_SIZE/*alignment*/, 200000 * sizeof(uint64_t)/*size*/);
您的问题与所代表的问题非常相似。签出后,您将能够几乎准确地映射代码上的优化。您的内存带宽有限,因为您尝试进行的并行计算非常简单,基本上只是在内存位置之间移动数据。添加线程将导致内存缓存未命中/抖动和预取失败。这样做的结果是代码运行速度变慢。在现代PC上,大约有1.5个线程会使内存总线饱和。@RichardCritten,这不是真的。高端处理器的带宽上限设计为需要使用多线程来饱和它们。查看我答案上的链接。你如何衡量执行时间?@hristoilev我正在使用
perf stat-r200
来计算执行时间的平均值。你能给出一个计算结果吗?循环的绝对执行时间是多少秒?这是代码中唯一的OpenMP区域吗?它在一个程序运行中执行了多少次?这可能是OpenMP的开销。除此之外,对流的深入讨论包括线程数量的选择及其在核心之间的分布。每个核心有多个线程几乎肯定会减慢速度。设置omp parallel时是否维护simd矢量化?您可以使用omp for simd
请求多线程和矢量化,omp for simd是您正在使用的和omp simd
的组合。检查一下。编译器可能能够在没有任何提示的情况下对其进行矢量化,但如果您有可能指定它,那么最好也使用该子句。GCC的默认循环计划是static
,因此,在大约25000条缓存线中,有7条缓存线可能在线程之间共享,而在每个线程大约25000条缓存线中,有多达8条缓存线可能被错误共享。问题很可能是使用clock()
来测量Linux上的执行时间,这里有无数类似的问题。除了维护simd矢量化,使用非临时存储以获得高效的多线程执行也很重要。正如赫里斯托所说,如果你把所有线程的时间加起来,你肯定会看到它随着线程数的增加而增加。