OpenMP 并行代码运行速度较慢

Posted

技术标签:

【中文标题】OpenMP 并行代码运行速度较慢【英文标题】:OpenMP Parallelized Code Runs Slower 【发布时间】:2013-08-06 00:53:40 【问题描述】:

我已经看过很多这样的帖子,尽管阅读了很多内容,但我似乎无法在 OpenMP 中正确并行化以下代码,因为串行版本目前的运行速度比这快得多:

static double red_black_parallel_for_step(simulation* simObj, double stepSize, double* red, double* black)
double tmp = 0.0;   
double avg = 0.0;
double old = 0.0;
double max = -HUGE_VAL;
#pragma omp parallel \
shared(black, red, max) \
firstprivate(old, avg, tmp) 

    double priv_max = -HUGE_VAL;
    #pragma omp for 
    for(unsigned int j = 0; j < (*simObj).NY+2; j++)
        for(unsigned int i = 0; i < (int)floor((double)((*simObj).NX+2.0)/2.0); i++)
            for(unsigned int k = 1; k < (*simObj).NZ; k++)
                if(red[IX3] == HUGE_VAL) continue;
                old = red[IX3];
                avg = 0.0;
                const int x1 = ( black[IX3+IX3_XR1STEP] != HUGE_VAL ); 
                const int x2 = ( black[IX3+IX3_XR2STEP] != HUGE_VAL ); 
                const int y1 = ( black[IX3+IX3_YSTEP]   != HUGE_VAL ); 
                const int y2 = ( black[IX3-IX3_YSTEP]   != HUGE_VAL ); 
                const int z1 = ( black[IX3+IX3_ZSTEP]   != HUGE_VAL );
                const int z2 = ( black[IX3-IX3_ZSTEP]   != HUGE_VAL );
                if (x1) avg += black[IX3+IX3_XR1STEP];
                if (x2) avg += black[IX3+IX3_XR2STEP];
                if (y1) avg += black[IX3+IX3_YSTEP];
                if (y2) avg += black[IX3-IX3_YSTEP];
                if (z1) avg += black[IX3+IX3_ZSTEP];
                if (z2) avg += black[IX3-IX3_ZSTEP];
                avg /= (double) (x1+x2+y1+y2+z1+z2);
                red[IX3] = old + stepSize * (avg - old);
                tmp = fabs(old - red[IX3]) / fabs(old);
                if( tmp > priv_max ) priv_max = tmp;
            
        
    
    #pragma omp flush (max)
    if ( priv_max > max ) 
        #pragma omp critical
        
            if ( priv_max > max ) max = priv_max;
        
    

#pragma omp parallel \
shared(black, red, max) \
firstprivate(old, avg, tmp) 

    double priv_max = -HUGE_VAL;
    #pragma omp for 
    for(unsigned int j = 0; j < (*simObj).NY+2; j++)
        for(unsigned int i = 0; i < (int)floor((double)((*simObj).NX+2)/2.0); i++)
            for (unsigned int k = 1; k < (*simObj).NZ; k++ )   
                if ( black[IX3] == HUGE_VAL ) continue;
                old = black[IX3];
                avg = 0.0;
                const int x1 = ( red[IX3+IX3_XB1STEP] != HUGE_VAL ); 
                const int x2 = ( red[IX3+IX3_XB2STEP] != HUGE_VAL ); 
                const int y1 = ( red[IX3+IX3_YSTEP]   != HUGE_VAL ); 
                const int y2 = ( red[IX3-IX3_YSTEP]   != HUGE_VAL ); 
                const int z1 = ( red[IX3+IX3_ZSTEP]   != HUGE_VAL );
                const int z2 = ( red[IX3-IX3_ZSTEP]   != HUGE_VAL );
                if (x1) avg += red[IX3+IX3_XB1STEP];
                if (x2) avg += red[IX3+IX3_XB2STEP];
                if (y1) avg += red[IX3+IX3_YSTEP];
                if (y2) avg += red[IX3-IX3_YSTEP];
                if (z1) avg += red[IX3+IX3_ZSTEP];
                if (z2) avg += red[IX3-IX3_ZSTEP];
                avg /= (double) (x1+x2+y1+y2+z1+z2);
                black[IX3] = old + stepSize * (avg - old);
                tmp = fabs(old - black[IX3]) / fabs(old); 
                if( tmp > priv_max ) priv_max = tmp;
            
        
    
    #pragma omp flush (max)
    if ( priv_max > max ) 
        #pragma omp critical
        
            if ( priv_max > max ) max = priv_max;
        
    

return max;

复杂的因素是我需要跟踪红/黑迭代之间的最大相对变化(最大值)。任何帮助将非常感激。

【问题讨论】:

如果不阅读您的代码,您的 prolly 遇到的问题是,在多处理器上工作时,您必须将数据复制到不同的地方,很多时候这将超过并行化的收益 我会为所有迭代分别保存priv_max 的值,然后在最后找到这些值的最大值。如果您有如此多的迭代,您仍然需要/想要并行执行此操作,您需要通过几个步骤来完成 - 例如,将 priv_max 值的数组分解为 N 个片段。并行找到每个部分的最大值,然后依次找到 N 个中的最大值。对于N,您可能需要致电omp_get_max_threads() 谢谢@JerryCoffin。如您所见,每个线程都有自己的 priv_max 版本,并且在最后一个(外部)循环之后,这些线程被协调以选择最大的,max 被分配给它。只需 7 次调用此函数,并行版本大约需要 45 秒,串行运行需要 17 秒。循环迭代的总数约为 10^7。我不认为为协调不同的 priv_max 添加的位可以解释这一点。 @Ryan:抱歉——没有像我应该的那样仔细阅读代码。再看一遍,我能给出的唯一建议是发布sscce 以允许对代码进行试验。 @JerryCoffin:好的,我可以在 sscce 上工作。同时,我想知道 continue 语句是否会给 OpenMP 带来问题? 【参考方案1】:

仅在比较之后尝试刷新,并且仅在关键块内:

/* not here: #pragma omp flush (max) */
if ( priv_max > max )  // this should filter out most of the flush operations
    #pragma omp critical
    
        if ( priv_max > max ) max = priv_max; // now flush; this operation will be exclusive/"critical"
        #pragma omp flush (max)
    

【讨论】:

以上是关于OpenMP 并行代码运行速度较慢的主要内容,如果未能解决你的问题,请参考以下文章

在 OpenMP 并行代码中,memset 并行运行有啥好处吗?

在 OpenMP 中,我们如何并行运行多个代码块,其中每个代码块包含 omp single 和 omp for 循环?

如何使用 MPI 和 OpenMP 运行并行循环

OpenMP 循环运行代码比串行循环慢

在 Python 多处理进程中运行较慢的 OpenCV 代码片段

在运行时本地启用/禁用 OpenMP