我正在尝试使用 OpenMP 对已经矢量化的代码进行内部函数并行化,但问题是我使用一个 XMM 寄存器作为外部“变量”,我会在每个循环中递增。现在我正在使用 shared
子句
__m128d xmm0 = _mm_setzero_pd();
__declspec(align(16)) double res[2];
#pragma omp parallel for shared(xmm0)
for (int i = 0; i < len; i++)
{
__m128d xmm7 = ... result of some operations
xmm0 = _mm_add_pd(xmm0, xmm7);
}
_mm_store_pd(res, xmm0);
double final_result = res[0] + res[1];
因为不支持atomic
操作(在VS2010中)
__m128d xmm0 = _mm_setzero_pd();
__declspec(align(16)) double res[2];
#pragma omp parallel for
for (int i = 0; i < len; i++)
{
__m128d xmm7 = ... result of some operations
#pragma omp atomic
xmm0 = _mm_add_pd(xmm0, xmm7);
}
_mm_store_pd(res, xmm0);
double final_result = res[0] + res[1];
有谁知道一个巧妙的解决方法?
编辑:我刚才也使用并行模式库尝试过:
__declspec(align(16)) double res[2];
combinable<__m128d> xmm0_comb([](){return _mm_setzero_pd();});
parallel_for(0, len, 1, [&xmm0_comb, ...](int i)
{
__m128d xmm7 = ... result of some operations
__m128d& xmm0 = xmm0_comb.local();
xmm0 = _mm_add_pd(xmm0, xmm7);
});
__m128d xmm0 = xmm0_comb.combine([](__m128d a, __m128d b){return _mm_add_pd(a, b);});
_mm_store_pd(res, xmm0);
double final_result = res[0] + res[1];
但结果却令人失望。
您正在以错误的方式解决问题。您应该使用归约而不是原子操作:
这是一个更好的方法:
double sum = 0;
#pragma omp parallel for reduction(+:sum)
for (int i = 0; i < len; i++)
{
__m128d xmm7;// = ... result of some operations
// Collapse to a "double".
_declspec(align(16)) double res[2];
_mm_store_pd(res, xmm7);
// Add to reduction variable.
sum += res[0] + res[1];
}
double final_result = sum;
缩减本质上是一种操作,它使用 +
等关联操作将所有内容“缩减”为单个变量。
如果您要进行缩减,请始终尝试使用实际的缩减方法。不要试图用原子操作或关键部分来欺骗它。
这样做的原因是原子/关键部分方法本质上是不可扩展的,因为它们保持了长期的关键路径数据依赖性。适当的缩减方法将这条关键路径缩减为 log(# of threads)
。
当然,唯一的缺点是它破坏了浮点关联性。如果这很重要,那么您基本上只能按顺序总结每次迭代。
我是一名优秀的程序员,十分优秀!