我正在学习omp并遇到这个问题...... 考虑以下代码:
a
、b
和 c
已初始化#pragma omp parallel num_threads(4)
{
#pragma omp for schedule(static, 64)
for(int i = 0; i < 256; i++)
{
d[i] = a[i];
if( i + 1 < 256 )
d[i+1] = b[i];
if( i + 2 < 256 )
d[i+2] = c[i];
}
}
多次运行此代码时,注意到以下观察结果:
d
=64 或 i
=128处的
i
考虑到分配的线程数量 (4) 和迭代次数 (256),我应用了块大小为 64 的
schedule(static, 64)
。我假设使用 static
进行调度将使线程按顺序运行。我什至在作业上应用了#pragma omp critical
,但这不起作用,随机行为仍然存在。
您的
schedule(static, 64)
仅表明 256 次迭代应以 64 次为一组进行分发,schedule
子句),并且它不保证线程将以任何固定顺序执行。我发现这两篇文章很好地解释了这一点:
所以,你有一个竞争条件+数据竞争(谢谢Joachim)。即使你添加了,你仍然会遇到这些问题
#pragma omp critical
d[i] = a[i];
if( i + 1 < 256 )
#pragma omp critical
d[i+1] = b[i];
if( i + 2 < 256 )
#pragma omp critical
d[i+2] = c[i];
这意味着一次只允许 1 个线程执行这 3 条指令之一。但考虑到执行迭代 69 的线程 1 在线上
d[i+1] = b[i];
上,执行迭代 68 的线程 2 在线上 d[i+2] = c[i];
上。仍然是比赛状态。
通过添加 OpenMP 指令,您可以断言代码在并行执行时不会出现数据争用。 该代码具有循环携带依赖性,这将导致并行执行期间的数据竞争。您可以轻松地将循环转换为无依赖性(因此无数据竞争)循环:
#pragma omp parallel num_threads(4)
{
#pragma omp for schedule(static, 64)
for(int i = 0; i < 256; i++)
{
d[i] = a[i];
/* apply i:=i-1 to move the access to d into the same iteration */
if( i < 256 && i - 1 >= 0 )
d[i] = b[i - 1];
/* apply i:=i-2 to move the access to d into the same iteration */
if( i < 256 && i - 2 >= 0 )
d[i] = c[i - 2];
}
}
此转换解决了代码中的数据争用,而无需添加进一步的同步。编译器应该能够自动应用此类转换,并且实际上执行此类转换以向量化代码。由于您断言使用 OpenMP 不需要进行此类转换,因此编译器将不会应用该转换。