高速コーナーの最適化を続行することに決め、
_mm_movemask_epi8
SSE 命令でスタックしました。uint8x16_t
入力を使用してARM Neon用に書き直すにはどうすればよいですか?
5 に答える
この投稿がかなり古くなっていることは知っていますが、(検証済みの)解決策を提供することは有用であることがわかりました。入力引数のすべてのレーンですべて 1/0 を想定しています。
const uint8_t __attribute__ ((aligned (16))) _Powers[16]=
{ 1, 2, 4, 8, 16, 32, 64, 128, 1, 2, 4, 8, 16, 32, 64, 128 };
// Set the powers of 2 (do it once for all, if applicable)
uint8x16_t Powers= vld1q_u8(_Powers);
// Compute the mask from the input
uint64x2_t Mask= vpaddlq_u32(vpaddlq_u16(vpaddlq_u8(vandq_u8(Input, Powers))));
// Get the resulting bytes
uint16_t Output;
vst1q_lane_u8((uint8_t*)&Output + 0, (uint8x16_t)Mask, 0);
vst1q_lane_u8((uint8_t*)&Output + 1, (uint8x16_t)Mask, 8);
(とにかくhttp://gcc.gnu.org/bugzilla/show_bug.cgi?id=47553に注意してください。)
Michael と同様に、秘訣は、null 以外のエントリのインデックスのベキを形成し、ペアごとに 3 回合計することです。これは、データ サイズを増やして、追加ごとにストライドを 2 倍にする必要があります。2 x 8 8 ビット エントリから 2 x 4 16 ビットに減らし、次に 2 x 2 32 ビットと 2 x 1 64 ビットに減らします。これら 2 つの数値の下位バイトが解を示します。NEON を使用してそれらをまとめて単一の短い値を形成する簡単な方法はないと思います。
入力が適切な形式で、パワーをプリロードできる場合は、6 つの NEON 命令を実行します。
いくつかのテストの後、次のコードが正しく機能するように見えます。
int32_t _mm_movemask_epi8_neon(uint8x16_t input)
{
const int8_t __attribute__ ((aligned (16))) xr[8] = {-7,-6,-5,-4,-3,-2,-1,0};
uint8x8_t mask_and = vdup_n_u8(0x80);
int8x8_t mask_shift = vld1_s8(xr);
uint8x8_t lo = vget_low_u8(input);
uint8x8_t hi = vget_high_u8(input);
lo = vand_u8(lo, mask_and);
lo = vshl_u8(lo, mask_shift);
hi = vand_u8(hi, mask_and);
hi = vshl_u8(hi, mask_shift);
lo = vpadd_u8(lo,lo);
lo = vpadd_u8(lo,lo);
lo = vpadd_u8(lo,lo);
hi = vpadd_u8(hi,hi);
hi = vpadd_u8(hi,hi);
hi = vpadd_u8(hi,hi);
return ((hi[0] << 8) | (lo[0] & 0xFF));
}
私はこれをテストしていませんが、次のようなものが機能する可能性があることに注意してください。
X := the vector that you want to create the mask from
A := 0x808080808080...
B := 0x00FFFEFDFCFB... (i.e. 0,-1,-2,-3,...)
X = vand_u8(X, A); // Keep d7 of each byte in X
X = vshl_u8(X, B); // X[7]>>=0; X[6]>>=1; X[5]>>=2; ...
// Each byte of X now contains its msb shifted 7-N bits to the right, where N
// is the byte index.
// Do 3 pairwise adds in order to pack all these into X[0]
X = vpadd_u8(X, X);
X = vpadd_u8(X, X);
X = vpadd_u8(X, X);
// X[0] should now contain the mask. Clear the remaining bytes if necessary
vpadd
これは64 ビットのベクトルでのみ機能するため、128 ビットのベクトルを処理するには、これを 1 回繰り返す必要があります。