簡體   English   中英

SSE2打包8位整數有符號乘法(高半):將m128i(16x8位)分解為兩個m128i(每個8x16)並重新打包

[英]SSE2 packed 8-bit integer signed multiply (high-half): Decomposing a m128i (16x8 bit) into two m128i (8x16 each) and repack

我試圖將每字節兩個m128i字節乘以(8位有符號整數)。

這里的問題是溢出。 我的解決方案是將這些8位有符號整數存儲到16位有符號整數中,然后將整個事物打包成16 x 8位整數的m128i

這是我做的__m128i mulhi_epi8(__m128i a, __m128i b)仿真:

inline __m128i mulhi_epi8(__m128i a, __m128i b)
{
    auto a_decomposed = decompose_epi8(a);
    auto b_decomposed = decompose_epi8(b);

    __m128i r1 = _mm_mullo_epi16(a_decomposed.first, b_decomposed.first);
    __m128i r2 = _mm_mullo_epi16(a_decomposed.second, b_decomposed.second);

    return _mm_packs_epi16(_mm_srai_epi16(r1, 8), _mm_srai_epi16(r2, 8));
}

decompose_epi8以非simd方式實現:

inline std::pair<__m128i, __m128i> decompose_epi8(__m128i input)
{
    std::pair<__m128i, __m128i> result;

    // result.first     =>  should contain 8 shorts in [-128, 127] (8 first bytes of the input)
    // result.second    =>  should contain 8 shorts in [-128, 127] (8 last bytes of the input)

    for (int i = 0; i < 8; ++i)
    {
        result.first.m128i_i16[i]   = input.m128i_i8[i];
        result.second.m128i_i16[i]  = input.m128i_i8[i + 8];
    }

    return result;
}

這段代碼效果很好。 我現在的目標是實現這個for循環的simd版本。 我查看了英特爾內部指南,但我找不到辦法做到這一點。 我想shuffle可以做到這一點,但我很難理解這一點。

如果你想進行有符號乘法,你需要將每個字節符號擴展為16位字,或者將它們移動到每個16位字的上半部分。 由於之后將結果打包在一起,您可以將輸入拆分為奇數和偶數字節,而不是上半部分和下半部分。 然后奇數字節的符號擴展可以通過算術地將所有16位部分向右移位來完成。 您可以通過屏蔽偶數字節來提取奇數字節,並且為了獲得偶數字節,您可以將所有16位部分向左移位(兩者都需要乘以_mm_mulhi_epi16 )。

以下內容適用於SSE2:

__m128i mulhi_epi8(__m128i a, __m128i b)
{
    __m128i mask = _mm_set1_epi16(0xff00);
    // mask higher bytes:
    __m128i a_hi = _mm_and_si128(a, mask);
    __m128i b_hi = _mm_and_si128(b, mask);

    __m128i r_hi = _mm_mulhi_epi16(a_hi, b_hi);
    // mask out garbage in lower half:
    r_hi = _mm_and_si128(r_hi, mask);

    // shift lower bytes to upper half
    __m128i a_lo = _mm_slli_epi16(a,8);
    __m128i b_lo = _mm_slli_epi16(b,8);
    __m128i r_lo = _mm_mulhi_epi16(a_lo, b_lo);
    // shift result to the lower half:
    r_lo = _mm_srli_epi16(r_lo,8);

    // join result and return:
    return _mm_or_si128(r_hi, r_lo);
}

注意:先前版本使用shift來對奇數字節進行符號擴展。 在大多數英特爾CPU上,這會增加P0的使用(也需要用於乘法)。 位邏輯可以在更多端口上運行,因此該版本應該具有更好的吞吐量。

暫無
暫無

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

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