short
タイプ(または)の宛先配列に追加する必要がある非常に長いバイト配列がありますint
。そのような SSE 命令は存在しますか? それとも彼らのセットですか?
2 に答える
8 ビット値の各ベクトルを 16 ビット値の 2 つのベクトルにアンパックしてから、それらを追加する必要があります。
__m128i v = _mm_set_epi8(15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0);
__m128i vl = _mm_unpacklo_epi8(v, _mm_set1_epi8(0)); // vl = { 7, 6, 5, 4, 3, 2, 1, 0 }
__m128i vh = _mm_unpackhi_epi8(v, _mm_set1_epi8(0)); // vh = { 15, 14, 13, 12, 11, 10, 9, 8 }
ここv
で、 は 16 x 8 ビット値のベクトルでvl
、vh
は 8 x 16 ビット値の 2 つのアンパック ベクトルです。
8 ビット値は符号なしであると想定しているため、16 ビットにアンパックすると、上位バイトは 0 に設定されます (つまり、符号拡張なし)。
これらのベクトルの多くを合計して 32 ビットの結果を得たい場合は_mm_madd_epi16
、乗数 1 を使用するのが便利な方法です。
__m128i vsuml = _mm_set1_epi32(0);
__m128i vsumh = _mm_set1_epi32(0);
__m128i vsum;
int sum;
for (int i = 0; i < N; i += 16)
{
__m128i v = _mm_load_si128(&x[i]);
__m128i vl = _mm_unpacklo_epi8(v, _mm_set1_epi8(0));
__m128i vh = _mm_unpackhi_epi8(v, _mm_set1_epi8(0));
vsuml = _mm_add_epi32(vsuml, _mm_madd_epi16(vl, _mm_set1_epi16(1)));
vsumh = _mm_add_epi32(vsumh, _mm_madd_epi16(vh, _mm_set1_epi16(1)));
}
// do horizontal sum of 4 partial sums and store in scalar int
vsum = _mm_add_epi32(vsuml, vsumh);
vsum = _mm_add_epi32(vsum, _mm_srli_si128(vsum, 8));
vsum = _mm_add_epi32(vsum, _mm_srli_si128(vsum, 4));
sum = _mm_cvtsi128_si32(vsum);
ゼロ拡張ではなくバイトベクトルを符号拡張する必要がある場合は、pmovsxbw
( _mm_cvtepi8_epi16
)を使用します。hi/lo 命令をアンパックするのとは異なり、pmovsx は src レジスタの下位半分/4 分の 1/8 分の 1 からしか実行できません。
ただし、メモリから直接 pmovsx を実行することはできますが、組み込み関数によってこれは非常に扱いにくくなります。ほとんどの CPU では、シャッフル スループットはロード スループットよりも制限されるため、1 回のロードと 3 回のシャッフルを行うよりも、2 回のロードと pmovsx を実行する方がおそらく望ましいでしょう。