使用原子操作更新双精度数组

时间:2015-05-26 13:36:26

标签: c openmp atomic

我正在尝试使用OpenMP以并行方式在双精度数组中编写(更新)。我们的想法是,需要更新的元素可以多次更新,并且元素本身可以即时计算。这使得它非常容易出现竞争条件,除非我“锁定”与使用原子操作更新的元素相对应的内存区域。如下例所示:

#include<omp.h>
#include<stdlib.h>

int main()
{

    double *x=NULL, contribution = 1234.5;
    size_t xlen=1000, i, n;

    if ( (x = (double *) calloc(xlen,sizeof(double)) ) == NULL) exit(-1)

    #pragma omp parallel for shared(x) private(n,contribution)
    for (i=0; i<xlen; i++) {
        n = find_n_on_the_fly(i);

        #pragma omp atomic update
        x[n]+=contribution; // in the more complicated case contributions are different

    }

    return 0;
}

我仍在使用这种方法遇到竞争条件。我尝试使用关键部分,但它完全杀了我,因为数组很大,更新的数量也很大。

问题:这种方法有什么问题?有没有更好的方法来处理这个?

注意:为了解决这个问题,我通过为每个线程创建数组副本并稍后减少它们来做一些愚蠢的事情。但记忆限制不允许我走得更远。

2 个答案:

答案 0 :(得分:1)

对我来说,上面的代码似乎很有效。

但是,您有两种方法可以改进它:

1-使用名为_mm_malloc的内存分配函数,其中缓存行大小为输入之一,而不是您使用的calloc。你现在面临的最大问题是虚假分享。为了省略FS的影响,通过使用上述方法,您基本上强制底层库以每个元素驻留在缓存中的一行中的方式分配内存(或您的数组)。这样,线程不会争夺缓存行以检索两个不同的互变量。换句话说,它将阻止在缓存行中分配两个变量。这将提高多线程程序的性能。 Google _mm_malloc了解更多信息。但是,以下是基本用法。在大多数现代计算机中,缓存行为64。

#if defined(__INTEL_COMPILER) 
#include <malloc.h> 
#else 
#include <mm_malloc.h> 
#endif 

int CPU_Cache_line_size = 64;

int xlen = 100;
double *ptr = _mm_malloc(xlen*sizeof(double), CPU_Cache_line_size); 
/*
  do something ...
*/
_mm_free(ptr); 
可以通过以下方式查询

__CPU_CACHE_LINE_SIZE__

  • Linux命令:

    getconf LEVEL1_DCACHE_LINESIZE

  • 编程方式:

    int CPU_Cache_line_size = sysconf (_SC_LEVEL1_DCACHE_LINESIZE);

  • 有关缓存的详细信息:

    /sys/devices/system/cpu/cpu0/cache/

2-你自己提到了这一点,但是你的情况很严重:每个线程使用一个数组然后减少它们。这种方法更有效。如果可以,请再次考虑使用此方法。

答案 1 :(得分:0)

我将在前面加上“我对并行计算非常新”,根据您的使用情况,这可能不太可行。

可能有用的一个想法是分离程序的计算和更新方面。建立一个主/控制进程/线程,该进程/线程只能访问数组,并从从/计算进程/线程收集的信息中以队列方式执行更新。这可以用于最小化传统意义上的锁定需求。

我希望至少提供一种不同的方式来查看问题。