作者:至上励合_安儿_466 | 来源:互联网 | 2022-12-08 10:24
我想做的是:
将输入浮点数乘以固定因子.
将它们转换为8位有符号字符.
请注意,大多数输入具有较小的绝对值范围,如[-6,6],因此固定因子可以将它们映射到[-127,127].
我只使用avx2指令集,所以内在函数就像_mm256_cvtepi32_epi8
不能使用一样.我想使用,_mm256_packs_epi16
但它将两个输入混合在一起.:(
我还编写了一些将32位浮点数转换为16位int的代码,它正如我想要的那样工作.
void Quantize(const float* input, __m256i* output, float quant_mult, int num_rows, int width) {
// input is a matrix actuaaly, num_rows and width represent the number of rows and columns of the matrix
assert(width % 16 == 0);
int num_input_chunks = width / 16;
__m256 avx2_quant_mult = _mm256_set_ps(quant_mult, quant_mult, quant_mult, quant_mult,
quant_mult, quant_mult, quant_mult, quant_mult);
for (int i = 0; i
欢迎任何帮助,非常感谢你!
1> Peter Cordes..:
为了获得具有多个源向量的良好吞吐量,有两个输入向量而不是产生更窄的输出是一件好事_mm256_packs_epi16
.(AVX512 _mm256_cvtepi32_epi8
不一定是最有效的处理方式,因为具有内存目标的版本会解码为多个uop,或者常规版本会为您提供需要单独存储的多个小输出.)
或者你在抱怨它是如何在车道上运作的?是的,这很烦人,但_mm256_packs_epi32
做同样的事情.如果输出在那里有交错的数据组,那么也要做同样的事情.
你最好的办法就是将4个向量组合成1个,分为2个步道的包装(因为没有交叉包装).然后使用一个车道交叉shuffle来修复它.
#include
// loads 128 bytes = 32 floats
// converts and packs with signed saturation to 32 int8_t
__m256i pack_float_int8(const float*p) {
__m256i a = _mm256_cvtps_epi32(_mm256_loadu_ps(p));
__m256i b = _mm256_cvtps_epi32(_mm256_loadu_ps(p+8));
__m256i c = _mm256_cvtps_epi32(_mm256_loadu_ps(p+16));
__m256i d = _mm256_cvtps_epi32(_mm256_loadu_ps(p+24));
__m256i ab = _mm256_packs_epi32(a,b); // 16x int16_t
__m256i cd = _mm256_packs_epi32(c,d);
__m256i abcd = _mm256_packs_epi16(ab, cd); // 32x int8_t
// packed to one vector, but in [ a_lo, b_lo, c_lo, d_lo | a_hi, b_hi, c_hi, d_hi ] order
// if you can deal with that in-memory format (e.g. for later in-lane unpack), great, you're done
// but if you need sequential order, then vpermd:
__m256i lanefix = _mm256_permutevar8x32_epi32(abcd, _mm256_setr_epi32(0,4, 1,5, 2,6, 3,7));
return lanefix;
}
(在Godbolt编译器资源管理器上很好地编译).
在循环和_mm256_store_si256
结果向量中调用它.
每个256位存储共有4次shuffle,每次吞吐量1次shuffle将成为Intel CPU的瓶颈.您应该在每个时钟获得一个浮点向量的吞吐量,在端口5上出现瓶颈.(https://agner.org/optimize/).如果数据在L2中不热,或者可能会在内存带宽上出现瓶颈.
如果你只有一个单一的向量做,你可以考虑使用uint8_t
将每个epi32元素的低字节到每个通道的低32位,那么_mm256_packus_epi16
车道交叉.
另一个单向量替代方案(Ryzen的优点)是extracti128 + 128位packssdw + packsswb.但是,如果你只做一个向量,那仍然是好的.(仍然在Ryzen上,你会想要在128位向量中工作,以避免额外的跨越通道,因为Ryzen将每个256位指令分成(至少)2个128位uops.)
有关:
SSE - AVX从double转换为char
如何使用avx指令将float向量转换为short int?