[英]How does OpenMP use the atomic instruction inside reduction clause?
[英]OpenMP reduction clause does not work for a big long int for loop count
有以下一段代码:
#include <iostream>
#include <fstream>
#include <limits>
#include <chrono>
#include <omp.h>
const long int N=1000000000L;
class Counter {
public:
Counter():n(0),N(0){}
void operator()()
{
if(n==INT_MAX)
{
#pragma omp atomic update
++N;
#pragma omp atomic write
n=1;
}
else
{
#pragma omp atomic update
++n;
}
}
long int GetTotalCount()
{
return (static_cast<long int>(n)+static_cast<long int>(N)*static_cast<long int>(INT_MAX));
}
friend std::ostream & operator<<(std::ostream & o, Counter & counter)
{
o<<counter.GetTotalCount()<<" ("<<counter.N<<","<<counter.n<<") = "
<<static_cast<long int>(counter.N)*static_cast<long int>(counter.INT_MAX)+static_cast<long int>
(counter.n)
<<std::endl;
return o;
}
private:
const int INT_MAX=std::numeric_limits<int>::max();
int n;
int N;
};
int main(int argc, char **argv)
{
const auto begin = std::chrono::steady_clock::now();
Counter counter;
#pragma omp parallel for simd
for(long int i=0; i<N; ++i)
{
if(i%2==0) counter();
}
const auto end = std::chrono::steady_clock::now();
std::cout<<"N="<<N<<std::endl;
std::cout<<"Total count="<<counter;
auto time = std::chrono::duration_cast<std::chrono::milliseconds>(end-begin).count();
std::cout<<"t="<<time<<" ms"<<std::endl;
return 0;
}
它适用于所有 N <=1000000000 并给出以下 output:
N=1000000000
总计数=500000000 (0,500000000) = 500000000
t=11297 毫秒
但是如果要使 N 大 10 倍,则 output 不正确(第 2 行):
N=10000000000
总计数=705032704 (0,705032704) = 705032704
t=112660 毫秒
这里的第二行必须是
总计数=500000000 (2,500000000) = 705032706
我不明白为什么程序在 N=1e10 处无法正常运行,尽管 N 是long int 。
您在检查中有一个竞争条件:
if(n==INT_MAX)
{
// Nothing prevents another thread to read n here and enter the same branch
#pragma omp atomic update
++N;
#pragma omp atomic write
n=1;
}
因此,两个线程可能都决定同时重置n
和递增N
除此之外,您还必须以原子方式读取n
以获取 if 检查本身,尽管仅此还不够。
总体而言,您最好使用实际的 OpenMP 缩减或自定义构建的缩减以及支持实际原子操作的足够大的数据类型。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.