C++ 中两个 16 位整数向量与 AVX2 的内积
Inner product of two 16bit integer vectors with AVX2 in C++
我正在寻找最有效的方法来乘以两个对齐的 int16_t 数组,其长度可以用 AVX2 除以 16。
乘成向量后 x
我从 _mm256_extracti128_si256
和 _mm256_castsi256_si128
开始得到 x
的低和高部分,然后用 [=14= 添加它们].
我复制了结果寄存器并将 _mm_move_epi64
应用到原始寄存器并用 _mm_add_epi16
再次添加。现在,我想我有:
-, -, -, -, x15+x7+x11+x3, x14+x6+x10+x2, x13+x5+x9+x1, x12+x4+x8+x0
在 128 位寄存器内。但是现在我卡住了,不知道如何有效地总结剩余的四个条目以及如何提取16位结果。
根据 google 我的工作解决方案的评论和时间:
// 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)
我正在寻找最有效的方法来乘以两个对齐的 int16_t 数组,其长度可以用 AVX2 除以 16。
乘成向量后 x
我从 _mm256_extracti128_si256
和 _mm256_castsi256_si128
开始得到 x
的低和高部分,然后用 [=14= 添加它们].
我复制了结果寄存器并将 _mm_move_epi64
应用到原始寄存器并用 _mm_add_epi16
再次添加。现在,我想我有:
-, -, -, -, x15+x7+x11+x3, x14+x6+x10+x2, x13+x5+x9+x1, x12+x4+x8+x0
在 128 位寄存器内。但是现在我卡住了,不知道如何有效地总结剩余的四个条目以及如何提取16位结果。
根据 google 我的工作解决方案的评论和时间:
// 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)