OpenMP 实现缩减

Posted

技术标签:

【中文标题】OpenMP 实现缩减【英文标题】:OpenMP implementation of reduction 【发布时间】:2014-01-30 00:56:04 【问题描述】:

我需要实现归约操作(对于每个线程,值应该存储在不同的数组条目中)。但是,对于更多线程,它运行速度较慢。有什么建议吗?

double local_sum[16];.
//Initializations....
#pragma omp parallel for shared(h,n,a) private(x, thread_id) 
for (i = 1; i < n; i++) 
    thread_id = omp_get_thread_num();
    x = a  + i* h;
    local_sum[thread_id] += f(x);

【问题讨论】:

【参考方案1】:

您正在经历虚假分享的影响。在 x86 上,单个高速缓存行的长度为 64 字节,因此包含 64 / sizeof(double) = 8 个数组元素。当一个线程更新其元素时,它运行的核心使用缓存一致性协议使所有其他核心中的相同缓存行无效。当另一个线程更新它的元素,而不是直接在缓存上操作时,它的核心必须从上层数据缓存或主内存重新加载缓存行。这会显着减慢程序的执行速度。

最简单的解决方案是插入填充,从而将不同线程访问的数组元素分散到不同的缓存行中。在 x86 上,这将是 7 个 double 元素。因此,您的代码应如下所示:

double local_sum[8*16];
//Initializations....
#pragma omp parallel for shared(h,n,a) private(x, thread_id) 
for (i = 1; i < n; i++) 
    thread_id = omp_get_thread_num();
    x = a  + i* h;
    local_sum[8*thread_id] += f(x);

在最后对数组求和时不要忘记只取第 8 个元素(或将所有数组元素初始化为零)。

【讨论】:

保留原始的 16 元素数组并在并行循环中使用私有局部部分和减少然后在并行循环之外但在并行块中填充 16 元素数组不是更好吗.这仍然存在错误共享,但影响可以忽略不计,因为每个线程只命中一次数组,而不是每次迭代一次,此外您不必担心 NUMA 系统上的页面。 这会更好,但它不会具有向 OP 教授关于虚假共享的教育价值。 NUMA 相关的优化稍后会出现:)【参考方案2】:

你尝试过使用归约吗?

double global_sum = 0.0;
#pragma omp parallel for shared(h,n,a) reduction(+:global_sum) 
for (i = 1; i < n; i++) 
    global_sum += f(a  + i* h);

但是,它运行缓慢可能还有很多其他原因。例如,如果您只有 2 个 CPU 内核,则不应创建 16 个线程,依此类推。

【讨论】:

OP 明确声明他必须使用数组来实现归约操作。

以上是关于OpenMP 实现缩减的主要内容,如果未能解决你的问题,请参考以下文章

是否可以使用 openmp 对数组进行缩减?

在 std::vector 上的 Openmp 和缩减?

英特尔编译器 (C++) 问题与 std::vector 上的 OpenMP 减少有关

OpenMp实现并行化

不了解使用最大运算 OpenMP 程序并行减少的输出?

OpenMP:共享同一算法的单线程和多线程实现