簡體   English   中英

如何將32位浮點數轉換為8位有符號字符?

[英]How to convert 32-bit float to 8-bit signed char?

我想做的是:

  1. 將輸入浮點數乘以固定因子。
  2. 將它們轉換為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 < num_rows; ++i) {
    const float* input_row = input + i * width;
    __m256i* output_row = output + i * num_input_chunks;
    for (int j = 0; j < num_input_chunks; ++j) {
      const float* x = input_row + j * 16;
      // Process 16 floats at once, since each __m256i can contain 16 16-bit integers.

      __m256 f_0 = _mm256_loadu_ps(x);
      __m256 f_1 = _mm256_loadu_ps(x + 8);

      __m256 m_0 = _mm256_mul_ps(f_0, avx2_quant_mult);
      __m256 m_1 = _mm256_mul_ps(f_1, avx2_quant_mult);

      __m256i i_0 = _mm256_cvtps_epi32(m_0);
      __m256i i_1 = _mm256_cvtps_epi32(m_1);

      *(output_row + j) = _mm256_packs_epi32(i_0, i_1);
    }
  }
}

歡迎任何幫助,非常感謝你!

為了獲得具有多個源向量的良好吞吐量, _mm256_packs_epi16具有2個輸入向量而不是產生更窄的輸出是一件好事 (AVX512 _mm256_cvtepi32_epi8不一定是最有效的方法,因為具有內存目標的版本會解碼為多個uop,或者常規版本會為您提供需要單獨存儲的多個小輸出。)

或者你在抱怨它是如何在車道上運作的? 是的,這很煩人,但_mm256_packs_epi32做同樣的事情。 如果輸出在那里有交錯的數據組,那么也要做同樣的事情。

你最好的辦法就是將4個向量組合成1個,分為2個步道的包裝(因為沒有交叉包裝)。 然后使用一個車道交叉shuffle來修復它。

#include <immintrin.h>
// 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


對於uint8_t無符號目的地 ,使用_mm256_packus_epi16進行16-> 8步驟並保持其他所有內容相同。我們仍然使用帶符號的32-> 16打包,因為16 - > u8 vpackuswb打包仍然將其epi16 輸入作為簽名。您需要-1被視為-1 ,而不是+0xFFFF ,對於無符號飽和,將其鉗制為0。)


每個256位存儲共有4次shuffle,每次吞吐量1次shuffle將成為Intel CPU的瓶頸。 每個時鍾應該獲得一個浮點向量的吞吐量,在端口5上出現瓶頸 https://agner.org/optimize/ )。 如果數據在L2中不熱,或者可能會在內存帶寬上出現瓶頸。


如果你只有一個矢量做,你可以考慮使用_mm256_shuffle_epi8把每個epi32元素的低字節到每個通道的低32位,然后_mm256_permutevar8x32_epi32車道交叉。

另一個單矢量替代品(Ryzen上的好)是extracti128 + 128位packssdw + packsswb。 但是,如果你只做一個向量,那仍然是好的。 (仍然在Ryzen上,你會想要在128位向量中工作,以避免額外的跨越通道的混亂,因為Ryzen將每個256位指令分成(至少)2個128位uops。)

有關:

請檢查IEEE754標准格式以存儲浮點值,首先要了解這個float和double如何在內存中存儲,然后你才知道如何將float或double轉換為char,這很簡單。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM