2014-01-30 72 views
0

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

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); 
} 

回答

4

您正在经历虚假分享的影响。在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元(或初始化所有的数组元素为零)。

+0

保留原始16元素数组并不是更好,而是在并行循环中使用私有局部部分和减少,然后在并行循环之外但在并行块中填充16元素数组。这仍然有错误的共享,但影响可以忽略不计,因为该数组只在每个线程中命中一次,而不是每次迭代一次,此外,您不必担心NUMA系统上的页面。 –

+1

这样做会更好,但它不会具有教导OP关于虚假分享的教育价值。 NUMA相关的优化后来:) –

-1

您是否尝试过使用缩小?

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); 
} 

Howerver可能有很多其他原因为什么它运行缓慢。例如,如果只有2个CPU内核等,则不应创建16个线程。

+1

OP明确指出他必须使用数组实现约简操作。 –