【发布时间】:2020-06-22 15:15:08
【问题描述】:
我有以下一段代码。它的想法很简单。我的完整程序中有数十亿个事件,我需要计算其中一些不使用 long int 类型的事件。所以,我必须使用 2 个 int 数字 HIT 和 COUNT 而不是 1 个 int 数字,因为会有一个1 int 变量溢出(非常大的循环计数)。
#include <fstream>
#include <cstring>
#include <cmath>
#include <random>
#include <limits>
#include <chrono>
using namespace std;
int N=1000000000;
long int K=20*N;
int HIT=0;
int COUNT=0;
long int MAX=std::numeric_limits<int>::max();
int main(int argc, char **argv)
{
auto begin=std::chrono::steady_clock::now();
for(long int i=0; i<K; ++i)
{
++HIT;
if(HIT == MAX)
{
++COUNT;
HIT=0;
cout<<"COUNT="<<COUNT<<endl;
}
}
auto end=std::chrono::steady_clock::now();
cout<<"HIT="<<HIT<<endl;
cout<<"COUNT="<<COUNT<<endl;
const long int Total = HIT+COUNT*MAX;
cout<<"Total="<<Total<<" MAX="<<MAX<<endl;
if(Total==K) cout<<"Total == K"<<endl;
else cout<<"Total != K"<<endl;
auto elapsed_ms=std::chrono::duration_cast<std::chrono::milliseconds>(end-begin);
std::cout<<"time="<<elapsed_ms.count()<<" ms"<<std::endl;
return 0;
}
代码在 1 个线程中正常工作,并给出以下输出:
COUNT=1
COUNT=2
COUNT=3
COUNT=4
COUNT=5
COUNT=6
COUNT=7
COUNT=8
COUNT=9
HIT=672647177
COUNT=9
Total=20000000000 MAX=2147483647
Total == K
time=30971 ms
如果可能的话,我需要让它使用 OpenMP 并行工作,而不是使用互斥锁或与编译器实现相关的一些函数。但是当我将其修改为:
#pragma omp parallel for simd reduction(+:HIT,COUNT)
for(long int i=0; i<K; ++i)
输出如下:
HIT=20000000000
COUNT=0
Total=20000000000 MAX=2147483647
Total == K
time=2771 ms
最后,当我将代码修改为:
#pragma omp parallel for simd reduction(+:HIT,COUNT)
for(long int i=0; i<K; ++i)
{
++HIT;
if(HIT == MAX)
{
++COUNT;
#pragma omp atomic write
HIT=0;
cout<<"COUNT="<<COUNT<<endl;
}
}
输出是:
COUNT=1
COUNT=1
COUNT=1
COUNT=1
COUNT=1
COUNT=1
COUNT=1
COUNT=1
HIT=2820130824
COUNT=8
Total=20000000000 MAX=2147483647
Total == K
time=4232 ms
谁能给我解释一下发生了什么以及为什么输出如此不同?
我需要使用 OpenMP 让代码正确并行运行,那么如何正确执行呢?
是
#pragma omp atomic write
正确还是应该写
#pragma omp atomic update?
是否可以对 OpenMP reduction 子句中已经存在的值编写 atomic 操作?
使用英特尔 C++ 2019 编译器。
g++不允许在
中使用simd#pragma omp parallel for simd reduction(+:HIT,COUNT)
如果删除 simd,代码使用 g++ 无法正常工作。
【问题讨论】:
-
我猜由于每个并行线程都有自己的
HIT变量(最初为零),并处理整个范围的子集(K / 线程数),你的条件@987654331 @ 不会发生。如果您改为检查((i + 1) % MAX) == 0,您可能不会遇到此问题。顺便说一句,您的代码确实在 GCC 10.1 上使用 simd 编译。 -
每个线程都有自己的 HIT 和 COUNT 变量副本(对于#pragma om parallel for 子句是私有的)。这就是为什么在最后一种情况下所有行都是 COUNT=1 的原因。但是,最后,对于顺序代码,COUNT=8 而不是 COUNT=9。为什么?
-
不幸的是,我不明白为什么使用 ((i+1)%MAX)==0 更好。而且我不能在我的真实代码中使用它。这只是一个小的复制示例。在实际代码中,我应该处理类似于 HIT 和 COUNT 的变量。
-
我有一个旧版本的 GCC 5.3.1。