【问题标题】:Optimize C for-loop performance with openmp使用 openmp 优化 C for 循环性能
【发布时间】:2019-11-01 15:07:36
【问题描述】:

我有一个简单的for-loop,它根据 数组X[n][d]的对应行。

array *function(X, n, d){
    double *array = calloc(n,sizeof(double));
    //#pragma omp parallel
    {
        //#pragma omp parallel for if(n>15000)
        for( i=0 ; i<n-1; i++)
        {
            //#pragma omp parallel for shared(X,j, i) reduction(+: sum)
            //#pragma omp parallel for if(d>100) reduction(+:distances[:n]) private(j)
            for ( j=0; j< d; j++)
            {
                array[i] += (pow(X[(j+1)*n-1]-X[j*n+i], 2));
            }
            array[i] = sqrt(array[i]);
        }
    }
    return array;
}

认为nn=100000 一样高,d 可以具有从d=2d=100 的预定义值。 function() 在每次 kth 迭代中被多次调用 (2^k)。所以模式是:在第一次迭代它被调用一次,在第二次迭代它被调用两次,在第三次迭代它被调用四次等等......而且n在每次迭代中减少一个(n-=1 )。

我尝试了 openmp 指令的不同组合,这些指令作为 cmets 放入示例代码中,但无论我尝试了什么,代码在没有 openmp 指令的情况下执行相同或更好。

使用openmp提高上述循环的时间性能有哪些好的方法/技术?

【问题讨论】:

  • 你检查过非openmp(但经过优化)生成的代码吗?如今的编译器非常擅长优化,这可能会造成一些差异。另一件可能相关的事情是使用 OpenMP 时线程之间的缓存和缓存一致性。
  • 我正在使用 -O3 优化标志进行编译。我怎么能检查优化的代码呢?我唯一知道的是我手写的顺序部分。
  • 你为什么要对一个值进行平方然后取其平方根? √(x²)x...
  • @Shawn 这是一个错字。我道歉
  • 您现在确定您的代码了吗?什么是distances,它与array 有什么联系?

标签: c loops for-loop nested openmp


【解决方案1】:

如果没有东西来测试它很难说,但我会尝试这样的事情:

double* function( double* X, int n, int d ) {
    double *array = malloc( n * sizeof( double ) );
    #pragma omp parallel for schedule( static )
    for( int i = 0 ; i < n - 1; i++ ) {
        double sum = 0;
        for( int j = 0; j < d; j++ ) {
           double dist = X[( j + 1 ) * n - 1] - X[j * n + i];
           sum += dist * dist;
        }
        array[i] = sqrt( sum );
    }
    return array;
}

我不确定它是否会比您的代码更有效,但它有一些改进应该会对性能产生影响:

  1. 为了避免在顺序部分将数组初始化为 0,并希望编译器能够更好地优化,我将 calloc() 替换为普通的 malloc() 并使用局部变量 sum 来累积部分和.
  2. 我已将并行化 pragma 尽可能放在最外层,以最大限度地提高并行度。
  3. 我使用了一个本地的dist 变量来存储X 的两个当前值之间的临时距离,然后将其乘以自身,避免了对更复杂的pow() 函数的昂贵调用。

现在,根据您从中获得的结果,您可以考虑添加与最初在 #pragma omp parallel for 指令中相同类型的 if( n &gt; NMIN ) 语句。此NMIN 的值将由您根据测量的性能来确定。 另一个可能的优化方向是将parallel 指令放在函数之外。这将使您免于无数线程启动/停止。但是,您必须在调用 malloc() 之前添加 #pragma omp single 并从现有指令中删除 parallel

【讨论】:

  • 谢谢。这增加了一些很好的改进。此外,您是否认为通过在第二个循环之前添加#pragma omp parallel for schedule(static) reduction(+:sum) 可以提高性能,更多(对于大d 说像d=100)?我试过添加它,但不幸的是我得到了一个巨大的摊位,我不明白为什么。您能否解释一下为什么您建议的这些改进提高了代码的性能?
  • “您认为在第二个循环之前添加#pragma omp parallel for schedule(static) reduction(+:sum) 可以提高性能”。既然您说“n”足够大(> 10 倍的核心数),您已经有足够的并行性来利用所有核心,因此通过尝试并行化内部循环来添加嵌套并行性是没有意义的)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-12-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多