AVX2向量化256位查找表(32位无符号字符)

问题描述:

我是AVX内部函数(和一般AVX)的新手,我试图加快一些代码,使用由32位无符号字符组成的256位查找表。目前,该代码(虚拟数据)被写成这样:AVX2向量化256位查找表(32位无符号字符)

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]]; 
} 

的正常工作,并在下面的结果被放入“结果”:

0, 4, 8, 12, 16, 20, 24, 28 

在试图加快这,我“取代VE上述代码用下面的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(根据psuedocode在https://software.intel.com/en-us/node/524017),有效地使任何指数高于16“环绕”再次,因此重复(0,4, 8,12)。

我使用错误的AVX呼叫了吗?我完全脱离了我认为这应该起作用的方式吗?

+0

这是不会工作。您可以尝试使用收集指令,但那些指令至少加载32位块,因此效率可能会有问题,特别是在收集指令缓慢的Haswell上。 – EOF

+0

总的想法是合理的,但你需要知道洗牌真的是2×128位操作,而不是一个正确的256位洗牌(就像很多其他AVX指令一样)。解决方案比上面的代码更复杂,但它应该比标量代码更高效。 –

+0

实际上,您最好做2 x 128位SSE混洗,并使用位4从两个16路查找中选择最终输出。 –

这是一个使用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 
+1

谢谢保罗,这非常有帮助(并且完美无缺!)。基于你以前的评论,我已经意识到我需要形成一个掩模,指出哪些指数大于16,但不知道blendv指令在那里,这是我失去的一块。再次感谢您的帮助! – user3062913