OpenMP atomic _mm_add_pd
OpenMP atomic _mm_add_pd
我试图使用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)
。
唯一的缺点当然是它破坏了浮点结合性。如果这很重要,那么您基本上只能按顺序对每个迭代求和。
你想要的是降价。如果您的编译器支持它(gcc支持),您可以将其作为omp缩减来执行,或者您可以通过将每个线程的总和添加到一个私有的xmm中来自己执行omp缩减。下面是一个简单的版本:
#include <emmintrin.h>
#include <omp.h>
#include <stdio.h>
int main(int argc, char **argv) {
const int NTHREADS=8;
const int len=100;
__m128d xmm0[NTHREADS];
__m128d xmmreduction = _mm_setzero_pd();
#pragma omp parallel for num_threads(NTHREADS)
for (int i=0; i<NTHREADS; i++)
xmm0[i]= _mm_setzero_pd();
__attribute((aligned(16))) double res[2];
#pragma omp parallel num_threads(NTHREADS) reduction(+:xmmreduction)
{
int tid = omp_get_thread_num();
#pragma omp for
for (int i = 0; i < len; i++)
{
double d = (double)i;
__m128d xmm7 = _mm_set_pd( d, 2.*d );
xmm0[tid] = _mm_add_pd(xmm0[tid], xmm7);
xmmreduction = _mm_add_pd(xmmreduction, xmm7);
}
}
for (int i=1; i<NTHREADS; i++)
xmm0[0] = _mm_add_pd(xmm0[0], xmm0[i]);
_mm_store_pd(res, xmm0[0]);
double final_result = res[0] + res[1];
printf("Expected result = %fn", 3.0*(len-1)*(len)/2);
printf("Calculated result = %lfn", final_result);
_mm_store_pd(res, xmmreduction);
final_result = res[0] + res[1];
printf("Calculated result (reduction) = %lfn", final_result);
return 0;
}
在回答我问题的人的大力帮助下,我想出了这个:
double final_result = 0.0;
#pragma omp parallel reduction(+:final_result)
{
__declspec(align(16)) double r[2];
__m128d xmm0 = _mm_setzero_pd();
#pragma omp for
for (int i = 0; i < len; i++)
{
__m128d xmm7 = ... result of some operations
xmm0 = _mm_add_pd(xmm0, xmm7);
}
_mm_store_pd(r, xmm0);
final_result += r[0] + r[1];
}
它基本上将坍缩和还原分离,性能很好。
非常感谢所有帮助过我的人!我猜你不能在编译器中添加自己的内在函数,而MS编译器决定跳过内联汇编器。我不确定是否有一个简单的解决方案。
相关文章:
- Visual Studio - Cmake Project - Add NetCDF
- 在 atomic() 中 ++、add operation 和 fetch_add() 有什么区别
- wxImageList::Add()始终返回0
- 函数 C::add() 中不允许将'C *'隐式转换为'A *'
- C++有"not equal compare and exchange"或"fetch add on not equal"吗?
- gRPC trace.cc 的方法 TraceFlagList::Add(TraceFlag* flag) 使其链接列表
- 如何将用户定义的格式标志"add" basic_ostream?
- 'Cannot add two pointers'添加带有 WCHAR 的 LPCWSTR
- 在创建完整对象之前编写 if-can-add check 方法的正确方法?
- CMake add library libpq (postgreSQL) mac c++ clion
- 如何将 4 个浮点数的 ps 向量转换为 4 个双精度并存储到 pd 数组
- 收到错误:C3867 '<int>Template_class::add':非标准语法;将指针指向带有模板的函数时
- 呼叫'php :: Extension :: add()没有匹配函数
- 使用intel内联汇编程序对bigint-add进行进位编码
- C++ 中头文件中的 Add 方法出错
- 二进制'+=':未找到采用类型 'Add' C++ 的全局运算符
- 如果我们使用链表数组来实现哈希表,则可以以不需要遍历的方式实现"add"。这是真的还是假的?
- 为什么 cmake 在定义 CXX 时忽略 ADD(SYSTEM) 头文件?
- "QFileSystemWatcher: failed to add paths"但仍然有效
- LNK2019反对CArray Add、GetAt、GetSize,所有包含都存在