2017-08-07 48 views
4

比方说,例如,我有2个变量__m256irowscols,在他们里面的值是:现在从`__m256i`使用值来访问有效的阵列 - SIMD

rows: 0, 2, 7, 5, 7, 2, 3, 0 
cols: 1, 2, 7, 5, 7, 2, 2, 6 

,这些值表示为8个点的xy位置,从而,在这种情况下,我将有以下几点:

p0: [0, 1], p1: [2, 2], p2: [7, 7], p3: [5, 5] 
p4: [7, 7], p5: [2, 2], p6: [3, 2], p7: [0, 6] 

我也有一个阵列称为lut将具有的值int类型:

lut: [0, 1, 2, 3, ..., 60, 61, 62, 63] 

我想要做的,是从rowscols变量使用这些位置值,用它访问lut阵列并创建一个新__m256i值与lut访问值。

我知道该怎么做,这将是存储rowscols值大小为8两个int阵列,然后在同一时间从lut阵列一个读出的值,然后使用_mm256_set_epi32()创建新_m256i价值的方式。

这有效,但在我看来是非常低效的..所以我的问题是如果有办法做得更快。

请注意,这些值仅仅是一个更具体的例子,并lut并不需要有有序值或大小64

的感谢!

+3

查找表通常在现代处理器上效率不高,因为它们总是创建数据依赖关系,所以如果您可以找到一种方法来用某种算术公式替换查找表,即使它有点复杂,它可以更快。否则,请参阅chtz的答案。 –

+0

向我们显示您的代码。比它可能给你一个优化提示。 – xMRi

+1

有时你可以将你的索引切分成4位的块,并用'pshufb'来获得一个16元的LUT。 (例如看看http://wm.ite.pl/articles/sse-popcount.html的SSE/AVX popcount实现。)但是当你的索引不是“可分离的”时,你必须用低字节进行多次混洗,并混合高字节,所以这是O(2 ^(n-4))适用于n位有效位,其中n> = 4。 –

回答

5

您可以构建使用avx2 gather instruction的解决方案,像这样

// index = (rows << 3) + cols; 
const __m256i index = _mm256_add_epi32(_mm256_slli_epi32(rows, 3), cols); 
// result = lut[index]; 
const __m256i result = _mm256_i32gather_epi32(lut, index, 4); 

要知道,目前CPU的收集指令有相当庞大的延迟,所以除非你可以实际使用result之前交错一些指令,这可能不是值得使用。

为了解释的4因素:scale因子在

__m256i _mm256_i32gather_epi32 (int const* base_addr, __m256i vindex, const int scale) 

被认为是实际的字节偏移量,即,用于每个索引的返回的值是:

*(const int*)((const char*) base_addr + scale*index) 

我不要知道这种行为是否有很多用例(也许这是为了能够使用1字节或2字节的条目访问LUT(以后需要一些掩码))。也许这只是允许的,因为可以缩放4,而缩放1/4或1/2不会(如果有人真的需要这个)。

+0

谢谢,这正是我想要的!只有一件事,我不明白为什么在这种情况下比例是4。你能向我解释为什么? –

+0

@ E.B。我添加了一个解释(虽然我不确定该行为的实际动机) – chtz

+1

解压缩/解码函数可能在向量中具有字节偏移量,而不是C样式的非缩放索引。我认为他们只是决定给你增加1,2,4或8倍的灵活性,因为他们在指令编码中有2位,它们可能用于通常的目的。译码一个VSIB字节与解码器(我假设为内部uop格式)几乎相同,是一个常规的SIB字节(用于常规非收集索引寻址模式,如'mov eax,[rdi + rcx * 4 + 1234] )。 –