ARM NEONの128bit演算とgcc (2)
比較演算
素直に比較すると要素ごとの比較になり、意図した結果にならない。
(そもそも結果はboolで欲しいのにベクトルになってしまう)
#include <arm_neon.h> int main() { uint32x4_t mm0={1,2,3,4}; uint32x4_t mm1={1,2,4,8}; uint32x4_t mm2=(mm0==mm1); uint32x4_t mm3=(mm0!=mm1); printf("eq? %08x,%08x,%08x,%08xn",mm2[0],mm2[1],mm2[2],mm2[3]); printf("ne? %08x,%08x,%08x,%08xn",mm3[0],mm3[1],mm3[2],mm3[3]); return 0; }
$ g++-4.8 -W -Wall -D_GCC_ -std=gnu++11 -O3 -mcpu=cortex-a7 -mfpu=neon-vfpv4 -mfloat-abi=hard -mvectorize-with-neon-quad -ffast-math -ftree-vectorizer-verbose=9 -o test2 test2.o $ ./test2 eq? ffffffff,ffffffff,00000000,00000000 ne? 00000000,00000000,ffffffff,ffffffff
得られた結果の and をとれば ==、or を取れば != が実現できるが、それなら最初から32bit単位で比較した方がマシ。
先人の記事
皆さん困っているようで、Stack Overflowにも記事があった。
- Fastest way to test a 128 bit NEON register for a value of 0 using intrinsics? – Stack Overflow
- vectorization – ARM NEON: comparing 128 bit values – Stack Overflow
上の記事は0との比較。2つの回答が寄せられている。
下記は短い方。VMRS命令は gccのARM NEON Intrinsicsには存在しないようなので、アセンブラで書かないといけない。
(cumulative saturation flag to be clearという指摘もある)
VTST.8 q1, q0, q0 VQADD.u8 q0, q1 VMRS r0,FPSCRFastest way to test a 128 bit NEON register for a value of 0 using intrinsics? – Stack Overflow
こちらは Intrinsics で書ける方。vtbl命令って高速なんでしょうか?
inline bool is_zero(int32x4_t v) noexcept { v = v == int32x4{}; return !int32x2_t( vtbl2_s8( int8x8x2_t{ int8x8_t(vget_low_s32(v)), int8x8_t(vget_high_s32(v)) }, int8x8_t{0, 4, 8, 12} ) )[0]; }Fastest way to test a 128 bit NEON register for a value of 0 using intrinsics? – Stack Overflow
32bit単位で処理
要素ごとの処理が意外に早い。vtblが遅いんだろうな。
| と || では、|| が良さそう。分岐がある方が早いのは意外。
bool is_zero2(uint32x4_t v) noexcept { return (v[0]|v[1]|v[2]|v[3])==0; } bool is_zero3(uint32x4_t v) noexcept { return !(v[0]||v[1]||v[2]||v[3]); }
任意の比較
単純に32bit単位要素比較するか、xorして0比較するか。
うちの環境では下記の3番目が良さそう。
bool equal_simple(uint32x4_t v, uint32x4_t w) noexcept { return ((v[0]==w[0])&&(v[1]==w[1])&&(v[2]==w[2])&&(v[3]==w[3])); } bool equal_xor(uint32x4_t v, uint32x4_t w) noexcept { uint32x4_t x = v^w; return !(x[0]|x[1]|x[2]|x[3]); } bool equal_xor2(uint32x4_t v, uint32x4_t w) noexcept { uint32x4_t x = v^w; return !(x[0]||x[1]||x[2]||x[3]); }
ディスカッション
コメント一覧
まだ、コメントがありません