AVX2 矢量化 256 位查找 table(32 个无符号字符)
AVX2 vectorized 256 bit lookup table (32 unsigned chars)
我是 AVX 内在函数(和一般的 AVX)的新手,我正在尝试加速一些使用由 32 个无符号字符组成的 256 位查找 table 的代码。目前代码(带有虚拟数据)是这样写的:
unsigned char lookup_table[32] = { 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 };
unsigned char result[8];
unsigned char indices[8] = { 0, 4, 8, 12, 16, 20, 24, 28};
for(int i = 0; i < 8; i++)
{
result[i] = lookup_table[indices[i]];
}
效果很好,结果将以下内容放入 "result":
0, 4, 8, 12, 16, 20, 24, 28
为了加快速度,我用以下 AVX 指令替换了上面的代码:
unsigned char lookup_table[32] = { 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 };
unsigned char result[8];
unsigned char indices[8] = { 0, 4, 8, 12, 16, 20, 24, 28};
__m256i avxTable = _mm256_loadu_si256((__m256i*)&table);
__m256i avxIndices = _mm256_loadu_si256((__m256i*)&indices);
__m256i avxResult= _mm256_shuffle_epi8(avxTable , avxIndices);
结果如下:
0, 4, 8, 12, 0, 4, 8, 12
我收集到的是 _mm256_shuffle_epi8 内在索引与 0X0F(根据 https://software.intel.com/en-us/node/524017 处的伪代码),有效地使任何索引再次超过 16 "wrap around",因此重复 (0, 4, 8, 12).
我使用了错误的 AVX 调用吗?我认为这应该有效吗?
这是一个使用 SSE 而不是 AVX 的解决方案。请注意,它并行执行 16 次查找(对于 128 位 SIMD 和 8 位元素,您不能做的比这少):
#include <stdio.h>
#include <smmintrin.h> // SSE 4.1
int main()
{
unsigned char lookup_table[32] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 };
unsigned char result[16];
unsigned char indices[16] = { 0, 4, 8, 12, 16, 20, 24, 28, 2, 6, 10, 14, 18, 22, 26, 30 };
__m128i vIndices, vSelect, vTable0, vTable1, vResult0, vResult1, vResult;
vIndices = _mm_loadu_si128((__m128i *)&indices);
vSelect = _mm_cmpgt_epi8(vIndices, _mm_set1_epi8(15));
vTable0 = _mm_loadu_si128((__m128i *)&lookup_table[0]);
vTable1 = _mm_loadu_si128((__m128i *)&lookup_table[16]);
vResult0 = _mm_shuffle_epi8(vTable0, vIndices);
vResult1 = _mm_shuffle_epi8(vTable1, vIndices);
vResult = _mm_blendv_epi8(vResult0, vResult1, vSelect);
_mm_storeu_si128((__m128i *)result, vResult);
printf("%vd\n", vResult);
return 0;
}
编译测试:
$ gcc -Wall test_lut.c -msse4 && ./a.out
0 4 8 12 16 20 24 28 2 6 10 14 18 22 26 30
我是 AVX 内在函数(和一般的 AVX)的新手,我正在尝试加速一些使用由 32 个无符号字符组成的 256 位查找 table 的代码。目前代码(带有虚拟数据)是这样写的:
unsigned char lookup_table[32] = { 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 };
unsigned char result[8];
unsigned char indices[8] = { 0, 4, 8, 12, 16, 20, 24, 28};
for(int i = 0; i < 8; i++)
{
result[i] = lookup_table[indices[i]];
}
效果很好,结果将以下内容放入 "result":
0, 4, 8, 12, 16, 20, 24, 28
为了加快速度,我用以下 AVX 指令替换了上面的代码:
unsigned char lookup_table[32] = { 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 };
unsigned char result[8];
unsigned char indices[8] = { 0, 4, 8, 12, 16, 20, 24, 28};
__m256i avxTable = _mm256_loadu_si256((__m256i*)&table);
__m256i avxIndices = _mm256_loadu_si256((__m256i*)&indices);
__m256i avxResult= _mm256_shuffle_epi8(avxTable , avxIndices);
结果如下:
0, 4, 8, 12, 0, 4, 8, 12
我收集到的是 _mm256_shuffle_epi8 内在索引与 0X0F(根据 https://software.intel.com/en-us/node/524017 处的伪代码),有效地使任何索引再次超过 16 "wrap around",因此重复 (0, 4, 8, 12).
我使用了错误的 AVX 调用吗?我认为这应该有效吗?
这是一个使用 SSE 而不是 AVX 的解决方案。请注意,它并行执行 16 次查找(对于 128 位 SIMD 和 8 位元素,您不能做的比这少):
#include <stdio.h>
#include <smmintrin.h> // SSE 4.1
int main()
{
unsigned char lookup_table[32] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 };
unsigned char result[16];
unsigned char indices[16] = { 0, 4, 8, 12, 16, 20, 24, 28, 2, 6, 10, 14, 18, 22, 26, 30 };
__m128i vIndices, vSelect, vTable0, vTable1, vResult0, vResult1, vResult;
vIndices = _mm_loadu_si128((__m128i *)&indices);
vSelect = _mm_cmpgt_epi8(vIndices, _mm_set1_epi8(15));
vTable0 = _mm_loadu_si128((__m128i *)&lookup_table[0]);
vTable1 = _mm_loadu_si128((__m128i *)&lookup_table[16]);
vResult0 = _mm_shuffle_epi8(vTable0, vIndices);
vResult1 = _mm_shuffle_epi8(vTable1, vIndices);
vResult = _mm_blendv_epi8(vResult0, vResult1, vSelect);
_mm_storeu_si128((__m128i *)result, vResult);
printf("%vd\n", vResult);
return 0;
}
编译测试:
$ gcc -Wall test_lut.c -msse4 && ./a.out
0 4 8 12 16 20 24 28 2 6 10 14 18 22 26 30