使用 OpenMP 计算嵌套循环内部

Counter inside nested loops with OpenMP

我在 c program for making raster image to see image statistic : relative size of each basin ( pixel counting)

中使用计数器

我的程序很长,所以我制作了Minimal, Reproducible Example

没有 OpenMP 的第一个程序显示了我想要实现的目标(所有像素的数量):

#include <stdio.h>
#include <stdlib.h>


int main()
{

    int x;
    int xMax = 100;
    int y;
    int yMax = 100;
    
    
    
    int i= 0; 

    for (x = 0; x < xMax; x++) {

        for (y = 0; y < yMax; y++)
            {i++;}
        }

    printf("i = %d \t xMax*yMax = %d\n", i, xMax*yMax);
    return 0;
}

它正确计算像素 (x,y):

 i = xMax*yMax

当我添加 OpenMP 时就没那么容易了,但减少会有所帮助

#include <stdio.h>
#include <stdlib.h>
#include <omp.h>        // OpenMP


int i= 0; 

int main()
{

    int x;
    int xMax = 1000;
    int y;
    int yMax = 1000;
    int all = xMax*yMax;
    
    
    #pragma omp parallel for collapse(2) schedule(dynamic) reduction(+:i)
    for (x = 0; x < xMax; x++) {
        for (y = 0; y < yMax; y++)
            {i++;}
        }

    printf("i = %d = %f*(xMax*yMax) \t where xMax*yMax = %d\n", i, (double)i/all,  all);
    return 0;
}

当我在另一个函数中隐藏计数器时,计数器未正确更新


#include <stdio.h>
#include <stdlib.h>
#include <omp.h>        // OpenMP


int i= 0; 



void P(){

 i++;

}




int main()
{

    int x;
    int xMax = 1000;
    int y;
    int yMax = 1000;
    int all = xMax*yMax;
    
    
    
    

    #pragma omp parallel for collapse(2) schedule(dynamic) reduction(+:i)
    for (x = 0; x < xMax; x++) {

        for (y = 0; y < yMax; y++)
            {P();}
        }

    printf("i = %d = %f*(xMax*yMax) \t where xMax*yMax = %d\n", i, (double)i/all,  all);
    return 0;
}

现在:

gcc p.c -Wall -fopenmp
./a.out
i = 437534 = 0.437534*(xMax*yMax)    where xMax*yMax = 1000000

问题:函数内的计数器未正确更新

问题:我应该更改什么才能正确更新计数器?

问题是 reduction(+:i) 子句创建了一个局部变量 i,您应该更改这个局部变量。但是,在您的代码中,您通过调用函数 P 来增加全局变量,这不是线程安全的(它在增加时有竞争条件)。因此,您只需确保在调用函数 P:

时增加本地 i
    void P(int& i){
       i++;
    }

    //in main:
    for (y = 0; y < yMax; y++)
            {P(i);}

另一个(但速度较慢)选项是通过使用原子操作使函数 P 线程安全。在这种情况下,您根本不需要减少:

void P(){
#pragma omp atomic
 i++;
}