gpt4 book ai didi

c++ - 如何向量化data_i16 [0至15]?

转载 作者:行者123 更新时间:2023-12-01 14:39:31 25 4
gpt4 key购买 nike

我在the Intel Intrinsic site上,我不知道我想要哪种指令组合。我想做的是

result = high_table[i8>>4] & low_table[i8&15]

两个表均为16位(或更多)。随机播放似乎是我想要的(_mm_shuffle_epi8),但是获取8bit值对我来说不起作用。似乎没有16位版本,非字节版本似乎需要第二个参数作为立即值。

我应该如何实现呢?我是否为每个表两次调用_mm_shuffle_epi8,将其强制转换为16位,然后将值移位8?如果是这样,我要看哪一个强制转换指令?

最佳答案

要将传入索引分为两个半字节 vector ,您需要通常的移位和AND。 SSE没有8位移位,因此您必须模拟更宽的移位和AND,以掩盖移入字节顶部的位。 (不幸的是,对于这种用例,_mm_shuffle_epi8不会忽略高位。如果设置了最高选择位,则该输出元素将为零。)

您绝对不希望将传入的i8 vector 扩展为16位元素。那将无法与_mm_shuffle_epi8一起使用。

AVX2具有vpermd:从8个32位元素的 vector 中选择dword。 (只有3位索引,因此除非您的半字节仅为0..7,否则这对您的用例不利)。 AVX512BW的混洗范围更广,包括vpermi2w可以索引到两个 vector 的串联表中,或者只是vpermw可以索引单词。

但是对于只有SSSE3的128位 vector ,是的,pshufb(_mm_shuffle_epi8)是可行的方法。对于high_table,您将需要两个单独的 vector ,一个 vector 用于每个单词条目的高字节,而另一个则用于低字节。还有两个 vector 用于low_table的一半。

使用_mm_unpacklo_epi8_mm_unpackhi_epi8交织两个 vector 的低8个字节,或两个 vector 的高8个字节。这将为您提供所需的16位LUT结果,每个字的上半部分来自上半部 vector 。

也就是说,您正在使用两个交错器从两个8位LUT中构建一个16位LUT。并且您要针对两个不同的LUT重复该过程两次。

该代码看起来像

// UNTESTED, haven't tried even compiling this.

// produces 2 output vectors, you might want to just put this in a loop instead of making a helper function for 1 vector.
// so I'll omit actually returning them.
void foo(__m128i indices)
{
// these optimize away, only used at compile time for the vector initializers
static const uint16_t high_table[16] = {...},
static const uint16_t low_table[16] = {...};

// each LUT needs a separate vector of high-byte and low-byte parts
// don't use SIMD intrinsics to load from the uint16_t tables and deinterleave at runtime, just get the same 16x 2 x 2 bytes of data into vector constants at compile time.
__m128i high_LUT_lobyte = _mm_setr_epi8(high_table[0]&0xff, high_table[1]&0xff, high_table[2]&0xff, ... );
__m128i high_LUT_hibyte = _mm_setr_epi8(high_table[0]>>8, high_table[1]>>8, high_table[2]>>8, ... );

__m128i low_LUT_lobyte = _mm_setr_epi8(low_table[0]&0xff, low_table[1]&0xff, low_table[2]&0xff, ... );
__m128i low_LUT_hibyte = _mm_setr_epi8(low_table[0]>>8, low_table[1]>>8, low_table[2]>>8, ... );


// split the input indexes: emulate byte shift with wider shift + AND
__m128i lo_idx = _mm_and_si128(indices, _mm_set1_epi8(0x0f));
__m128i hi_idx = _mm_and_si128(_mm_srli_epi32(indices, 4), _mm_set1_epi8(0x0f));

__m128i lolo = _mm_shuffle_epi8(low_LUT_lobyte, lo_idx);
__m128i lohi = _mm_shuffle_epi8(low_LUT_hibyte, lo_idx);

__m128i hilo = _mm_shuffle_epi8(high_LUT_lobyte, hi_idx);
__m128i hihi = _mm_shuffle_epi8(high_LUT_hibyte, hi_idx);

// interleave results of LUT lookups into vectors 16-bit elements
__m128i low_result_first = _mm_unpacklo_epi8(lolo, lohi);
__m128i low_result_second = _mm_unpackhi_epi8(lolo, lohi);
__m128i high_result_first = _mm_unpacklo_epi8(hilo, hihi);
__m128i high_result_second = _mm_unpackhi_epi8(hilo, hihi);

// first 8x 16-bit high_table[i8>>4] & low_table[i8&15] results
__m128i and_first = _mm_and_si128(low_result_first, high_result_first);
// second 8x 16-bit high_table[i8>>4] & low_table[i8&15] results
__m128i and_second = _mm_and_si128(low_result_second, high_result_second);

// TOOD: do something with the results.
}

在交织之前,您可以与高半对高,低半对低。对于指令级并行性,这可能会更好一些,让AND的执行与改组重叠。 (英特尔Haswell通过Skylake的洗牌只有1个时钟的吞吐量。)

选择变量名与诸如此类的事情很不容易。有些人只是放弃,并在某些中间步骤中使用了无意义的名称。

关于c++ - 如何向量化data_i16 [0至15]?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61436326/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com