【发布时间】:2021-01-20 21:53:48
【问题描述】:
我有这个函数来计算一个双精度数组:
void avx2_mul_64_block(double& sum, double* lhs_arr, double* rhs_arr) noexcept
{
__m256i accumulator = _mm256_setzero_pd();
for (std::size_t block = 0; block < 64; block += 4)
{
__m256i lhs = _mm256_set_pd(
lhs_arr[block ],
lhs_arr[block + 1],
lhs_arr[block + 2],
lhs_arr[block + 3]);
__m256i rhs = _mm256_set_pd(
rhs_arr[block ],
rhs_arr[block + 1],
rhs_arr[block + 2],
rhs_arr[block + 3]);
accumulator = _mm256_add_pd(accumulator, _mm256_mul_pd(lhs, rhs));
}
double* res = reinterpret_cast<double*>(&accumulator);
sum += res[0] + res[1] + res[2] + res[3];
}
,这段代码的性能不是我想要的。我相信让它成功 - 避免为其所有元素创建双数组,但我不知道如何做到这一点。
顺便说一句,与_mm256_setzero_si256相比,_mm256_setzero_pd 将整个功能减慢了一半。
我的标志:-O3 -ftree-vectorize -march=native
附:这不是真正的问题,只是设计问题。
【问题讨论】:
-
请提供minimal reproducible example,您的示例将无法编译。你在使用编译器优化吗?代码有多快?你希望它有多快?您的输入是否符合适当的界限?如果不是,他们会吗?
-
首先,您的注册类型错误。它应该是
__m256d,而不是__m256i。其次,使用正确对齐的输入数组(32 字节对齐),您应该使用_mm256_load_pd来获取数据。否则,您将依靠编译器来理解这是那些_mm256_set_pd调用试图实现的目标,而且可能不会。作为一个小的改进,您可以考虑对最终总和使用水平加法(使用_mm256_hadd_pd) -
你看生成的asm了吗? set_pd 将数字按内存的相反顺序排列。您还可以查看编译器为幼稚代码生成的 asm,自动矢量化应该可以很好地使用 -ffast-math。
-
@paddy:
_mm256_hadd_pd对于单个向量的有效水平总和并不是特别有用,除非您正在优化代码大小而不是速度。但是,是的,绝对使用load或loadu内部函数和__m256d,是的,hsum 可能会更好(参见 Get sum of values stored in __m256d with SSE/AVX) -
几乎是How do you load/store from/to an array of doubles with GNU C Vector Extensions? 的副本,但这只是因为我的答案有一个关于英特尔内在函数的部分,而问题要求没有内在函数的 GNU C 本机向量内容。也相关:Why doesn't gcc resolve _mm256_loadu_pd as single vmovupd? - 除了这似乎不是 GCC,可能是铿锵声。 GCC 不会让你摆脱 __m256i 和 __m256d 之间的隐式转换
标签: c++ x86 simd intrinsics avx