两个 16 位整数向量与 C++ 中的 AVX2 的内积
Posted
技术标签:
【中文标题】两个 16 位整数向量与 C++ 中的 AVX2 的内积【英文标题】:Inner product of two 16bit integer vectors with AVX2 in C++ 【发布时间】:2020-05-27 10:53:31 【问题描述】:我正在寻找最有效的方法来将两个对齐的 int16_t 数组相乘,其长度可以用 AVX2 除以 16。
在乘以向量x
后,我从_mm256_extracti128_si256
和_mm256_castsi256_si128
开始得到x
的低和高部分,然后用_mm_add_epi16
相加。
我复制了结果寄存器并将_mm_move_epi64
应用到原始寄存器并再次添加_mm_add_epi16
。现在,我想我有:-, -, -, -, x15+x7+x11+x3, x14+x6+x10+x2, x13+x5+x9+x1, x12+x4+x8+x0
在 128 位寄存器内。但是现在我卡住了,不知道如何有效地总结剩余的四个条目以及如何提取 16 位结果。
【问题讨论】:
_mm_move_epi64
不是水平总和的有用部分。此外,您没有描述如何从 16 位 int 元素中获得 _mm_add_ps
的浮点数。 _mm_add_epi16
用于水平总和的一步但_ps
用于另一步几乎没有意义。但无论如何,使用pmaddwd
进行整数乘法步骤以水平累加到 32 位元素中,将 hsum 简化为 32 位元素并提供更多范围而不会超限。无论如何,如果溢出不是问题,您想在垂直 pmaddwd
/ paddd
循环之后进行 hsum。
Fastest way to do horizontal SSE vector sum (or other reduction)
谢谢彼得,我知道这个帖子,但我在那里找不到我的特定数据类型的解决方案(如果答案已经存在,那么我不明白)。跨度>
是的,彼得,我犯了一些复制和粘贴错误,所以我没有对浮动进行任何转换。全部在 epi16/integer 中。
好的,如果这样是安全的(不会溢出),那么最好在循环中最初与 _mm256_madd_epi16
和 _mm_add_epi32
相乘,这样你就可以在外面使用 32 位 hsum .无论如何,实现 16 位 hsum 的最佳方法是使用 _mm_madd_epi16
作为第一步(如 hsum 链接中所述),但实际上使用它的全部功能作为您的乘法器而不是使用虚拟的 1
乘法器会更好。跨度>
【参考方案1】:
按照谷歌的 cmets 和小时数,我的工作解决方案:
// AVX multiply
hash = 1;
start1 = std::chrono::high_resolution_clock::now();
for(int i=0; i<2000000; i++)
ZTYPE* xv = al_entr1.c.data();
ZTYPE* yv = al_entr2.c.data();
__m256i tres = _mm256_setzero_si256();
for(int ii=0; ii < MAX_SIEVING_DIM; ii = ii+16/*8*/)
// editor's note: alignment required. Use loadu for unaligned
__m256i xr = _mm256_load_si256((__m256i*)(xv+ii));
__m256i yr = _mm256_load_si256((__m256i*)(yv+ii));
const __m256i tmp = _mm256_madd_epi16 (xr, yr);
tres = _mm256_add_epi32(tmp, tres);
// Reduction
const __m128i x128 = _mm_add_epi32 ( _mm256_extracti128_si256(tres, 1), _mm256_castsi256_si128(tres));
const __m128i x128_up = _mm_shuffle_epi32(x128, 78);
const __m128i x64 = _mm_add_epi32 (x128, x128_up);
const __m128i _x32 = _mm_hadd_epi32(x64, x64);
const int res = _mm_extract_epi32(_x32, 0);
hash |= res;
finish1 = std::chrono::high_resolution_clock::now();
elapsed1 = finish1 - start1;
std::cout << "AVX multiply: " <<elapsed1.count() << " sec. (" << hash << ")" << std::endl;
这至少是迄今为止最快的解决方案:
std::inner_product: 0.819781 秒。 (-14335) std::inner_product(对齐):0.964058 秒。 (-14335) 天真的乘法:0.588623 秒。 (-14335) 展开乘法:0.505639 秒。 (-14335) AVX 乘法:0.0488352 秒。 (-14335)【讨论】:
以上是关于两个 16 位整数向量与 C++ 中的 AVX2 的内积的主要内容,如果未能解决你的问题,请参考以下文章
用 SSE 在 C++ 中将两个 32 位整数向量相乘的最快方法