SSE2打包的8位整数有符号乘(高半):将m128i(16x8位)分解为两个m128i(每个8x16)并重新打包

时间:2019-07-31 13:32:09

标签: c++ intel sse simd intrinsics

我试图将每个字节的两个n = int(input("Wie viele Wortpaare sollen eingegeben werden: ")) a = [] for i in range(1,n+1): print("Bitte geben Sie das "+str(i)+"te Wortpaar ein:") a += [[input(),input()]] b = [] for m in range(0,len(a)): b += [a[m][1]] b += [a[m][0]] c = [] for x in range(len(a)-1,-1,-1): c += a[x] print(a) print(b) print(c) 字节(8位带符号整数)相乘。

这里的问题是溢出。我的解决方案是将这8位带符号整数存储为16位带符号整数,相乘,然后将整个内容打包为16 x 8位整数的m128i

这是我进行的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

此代码运行良好。我现在的目标是实现此for循环的simd版本。我看着Intel Intrinsics Guide,但找不到解决方法。我猜想shuffle可以解决问题,但是我很难在概念上做到这一点。

1 个答案:

答案 0 :(得分:3)

要进行有符号乘法时,需要将每个字节符号扩展为16位字,或者将它们移到每个16位字的上半部分。由于之后将结果打包在一起,因此可以将输入分成奇数和偶数字节,而不是上半部分和下半部分。 然后可以通过算术将所有16bit的部分向右移来完成奇数字节的符号扩展您可以通过屏蔽掉偶数字节来提取奇数字节,并且要获得偶数字节,可以进行移位左边的所有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来对奇数字节进行符号扩展。在大多数Intel CPU上,这会增加P0的使用率(也需要用于乘法)。比特逻辑可以在更多端口上运行,因此此版本应具有更好的吞吐量。