SSE4 で 16 個の符号なし 8 ビット整数のオブジェクトを乗算したいのですが__m128i
、16 ビット整数を乗算する組み込み関数しか見つかりませんでした。などはありません_mm_mult_epi8
か?
3 に答える
Agner Fog のソリューションに基づく Marat のソリューションよりも (潜在的に) 高速な方法:
ハイ/ローを分割する代わりに、奇数/偶数を分割します。これには、SSE4.1を必要とする代わりに純粋なSSE2で動作するという追加の利点があります(OPには役に立たないが、一部の人にとっては素晴らしい追加ボーナスです)。AVX2 をお持ちの場合は、最適化も追加しました。技術的には、AVX2 最適化は SSE2 組み込み関数でのみ機能しますが、左にシフトしてから右にシフトするソリューションよりも遅くなります。
__m128i mullo_epi8(__m128i a, __m128i b)
{
// unpack and multiply
__m128i dst_even = _mm_mullo_epi16(a, b);
__m128i dst_odd = _mm_mullo_epi16(_mm_srli_epi16(a, 8),_mm_srli_epi16(b, 8));
// repack
#ifdef __AVX2__
// only faster if have access to VPBROADCASTW
return _mm_or_si128(_mm_slli_epi16(dst_odd, 8), _mm_and_si128(dst_even, _mm_set1_epi16(0xFF)));
#else
return _mm_or_si128(_mm_slli_epi16(dst_odd, 8), _mm_srli_epi16(_mm_slli_epi16(dst_even,8), 8));
#endif
}
blendv_epi8
Agner は、SSE4.1 をサポートする組み込みを使用します。
編集:
興味深いことに、(最適化されたビルドで) さらに分解作業を行った後、少なくとも 2 つの実装はまったく同じものにコンパイルされます。「ivy-bridge」(AVX) を対象とした逆アセンブリの例。
vpmullw xmm2,xmm0,xmm1
vpsrlw xmm0,xmm0,0x8
vpsrlw xmm1,xmm1,0x8
vpmullw xmm0,xmm0,xmm1
vpsllw xmm0,xmm0,0x8
vpand xmm1,xmm2,XMMWORD PTR [rip+0x281]
vpor xmm0,xmm0,xmm1
コンパイル済みの 128 ビット xmm 定数を使用した「AVX2 最適化」バージョンを使用します。SSE2 サポートのみを使用してコンパイルすると、同様の結果が得られます (ただし、SSE2 命令を使用します)。私は、Agner Fog の元のソリューションが同じものに最適化される可能性があると考えています (そうでない場合はおかしくなります)。最適化されたビルドで Marat の元のソリューションがどのように比較されるかはわかりませんが、私にとっては、SSE2 よりも新しく、SSE2 を含むすべての x86 simd 拡張機能に対して単一のメソッドを持つことは非常に優れています。
MMX/SSE/AVX には 8 ビットの乗算はありません。ただし、次のように 16 ビット乗算を使用して、8 ビット乗算組み込み関数をエミュレートできます。
inline __m128i _mm_mullo_epi8(__m128i a, __m128i b)
{
__m128i zero = _mm_setzero_si128();
__m128i Alo = _mm_cvtepu8_epi16(a);
__m128i Ahi = _mm_unpackhi_epi8(a, zero);
__m128i Blo = _mm_cvtepu8_epi16(b);
__m128i Bhi = _mm_unpackhi_epi8(b, zero);
__m128i Clo = _mm_mullo_epi16(Alo, Blo);
__m128i Chi = _mm_mullo_epi16(Ahi, Bhi);
__m128i maskLo = _mm_set_epi8(0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 14, 12, 10, 8, 6, 4, 2, 0);
__m128i maskHi = _mm_set_epi8(14, 12, 10, 8, 6, 4, 2, 0, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80);
__m128i C = _mm_or_si128(_mm_shuffle_epi8(Clo, maskLo), _mm_shuffle_epi8(Chi, maskHi));
return C;
}
唯一の 8 ビット SSE 乗算命令はPMADDUBSW (SSSE3 以降、C/C++ 組み込み: _mm_maddubs_epi16 ) です。これは、16 x 8 ビットの符号なし値を 16 x 8 ビットの符号付き値で乗算し、隣接するペアを合計して 8 x 16 ビットの符号付き結果を生成します。この特殊な命令を使用できない場合は、16 ビット ベクトルのペアに展開し、通常の 16 ビット乗算命令を使用する必要があります。明らかに、これは少なくとも 2 倍のスループット ヒットを意味するため、可能であれば 8 ビットの乗算を使用してください。