我试图并行化在循环范围之外具有数据依赖关系(min)的程序的内部循环。我遇到了一个问题,在这个问题中,剩余的计算发生在内部j循环的范围之外。如果j循环中包含了“#杂注omp并行”部分,即使由于k值太低循环根本不运行,代码也会出错。比如说(1,2,3)。
for (i = 0; i < 10; i++)
{
#pragma omp parallel for shared(min) private (j, a, b, storer, arr) //
for (j = 0; j < k-4; j += 4)
{
mm_a = _mm_load_ps(&x[j]);
mm_b = _mm_load_ps(&y[j]);
mm_a = _mm_add_ps(mm_a, mm_b);
_mm_store_ps(storer, mm_a);
#pragma omp critical
{
if (storer[0] < min)
{
min = storer[0];
}
if (storer[1] < min)
{
min = storer[1];
}
//etc
}
}
do
{
#pragma omp critical
{
if (x[j]+y[j] < min)
{
min = x[j]+y[j];
}
}
}
} while (j++ < (k - 1));
round_min = min
}发布于 2021-04-01 11:41:19
j-based循环是一个并行循环,因此不能在循环之后使用。这一点尤其正确,因为您显式地将private,作为j,所以只在线程中本地可见,而不是在并行区域之外。您可以在并行循环之后使用j显式计算剩余(k-4+3)/4*4值的位置。
此外,以下是几点要点:
omp simd reduction。OpenMP可以为您完成计算剩余计算的所有无聊工作。此外,代码将是可移植的,而且要简单得多。生成的代码也可能比您的代码更快。但是要注意的是,一些编译器可能无法将代码向量化(GCC和ICC ),而Clang和MSVC通常需要一些help).omp critical)是非常昂贵的。在您的例子中,这只会消除与平行部分相关的任何可能的改进。由于缓存行bouncing._mm_store_ps在这里效率低下,代码可能会慢一些,尽管一些编译器(比如GCC)可能能够理解代码的逻辑并生成更快的实现(提取lane、data).下面是考虑到上述要点的修正代码:
for (i = 0; i < 10; i++)
{
// Assume min is already initialized correctly here
#pragma omp parallel for simd reduction(min:min) private(j)
for (j = 0; j < k; ++j)
{
const float tmp = x[j] + y[j];
if(tmp < min)
min = tmp;
}
// Use min here
}上述代码在GCC/ICC (均为x86 )、Clang (与-O3 -fopenmp -ffastmath)和MSVC (与/O2 /fp:precise -openmp:experimental)的体系结构上进行了正确的矢量化。
https://stackoverflow.com/questions/66869854
复制相似问题