从`__m256i`使用值来访问有效的阵列 - SIMD
比方说,例如,我有2个变量__m256i
叫rows
和cols
,在他们里面的值是:现在从`__m256i`使用值来访问有效的阵列 - SIMD
rows: 0, 2, 7, 5, 7, 2, 3, 0
cols: 1, 2, 7, 5, 7, 2, 2, 6
,这些值表示为8个点的x
和y
位置,从而,在这种情况下,我将有以下几点:
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]
我想要做的,是从rows
和cols
变量使用这些位置值,用它访问lut
阵列并创建一个新__m256i
值与lut
访问值。
我知道该怎么做,这将是存储rows
和cols
值大小为8两个int
阵列,然后在同一时间从lut
阵列一个读出的值,然后使用_mm256_set_epi32()
创建新_m256i
价值的方式。
这有效,但在我看来是非常低效的..所以我的问题是如果有办法做得更快。
请注意,这些值仅仅是一个更具体的例子,并lut
并不需要有有序值或大小64
的感谢!
您可以构建使用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不会(如果有人真的需要这个)。
谢谢,这正是我想要的!只有一件事,我不明白为什么在这种情况下比例是4。你能向我解释为什么? –
@ E.B。我添加了一个解释(虽然我不确定该行为的实际动机) – chtz
解压缩/解码函数可能在向量中具有字节偏移量,而不是C样式的非缩放索引。我认为他们只是决定给你增加1,2,4或8倍的灵活性,因为他们在指令编码中有2位,它们可能用于通常的目的。译码一个VSIB字节与解码器(我假设为内部uop格式)几乎相同,是一个常规的SIB字节(用于常规非收集索引寻址模式,如'mov eax,[rdi + rcx * 4 + 1234] )。 –
查找表通常在现代处理器上效率不高,因为它们总是创建数据依赖关系,所以如果您可以找到一种方法来用某种算术公式替换查找表,即使它有点复杂,它可以更快。否则,请参阅chtz的答案。 –
向我们显示您的代码。比它可能给你一个优化提示。 – xMRi
有时你可以将你的索引切分成4位的块,并用'pshufb'来获得一个16元的LUT。 (例如看看http://wm.ite.pl/articles/sse-popcount.html的SSE/AVX popcount实现。)但是当你的索引不是“可分离的”时,你必须用低字节进行多次混洗,并混合高字节,所以这是O(2 ^(n-4))适用于n位有效位,其中n> = 4。 –