ARM NEONの128bit演算とgcc (2)

2015/06/08Raspberry Pi, プログラミング

ARM NEONの128bit演算とgcc(メモ)の続き。




比較演算

素直に比較すると要素ごとの比較になり、意図した結果にならない。
(そもそも結果はboolで欲しいのにベクトルになってしまう)

[code lang="C"]
#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;
}
[/code]

$ 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にも記事があった。

上の記事は0との比較。2つの回答が寄せられている。
下記は短い方。VMRS命令は gccのARM NEON Intrinsicsには存在しないようなので、アセンブラで書かないといけない。
(cumulative saturation flag to be clearという指摘もある)

  VTST.8     q1, q0, q0  
  VQADD.u8   q0, q1
  VMRS       r0,FPSCR

Fastest way to test a 128 bit NEON register for a value of 0 using intrinsics? – Stack Overflow

こちらは Intrinsics で書ける方。vtbl命令って高速なんでしょうか?

[code lang="C"]
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];
}
[/code]
Fastest way to test a 128 bit NEON register for a value of 0 using intrinsics? – Stack Overflow

32bit単位で処理

要素ごとの処理が意外に早い。vtblが遅いんだろうな。
| と || では、|| が良さそう。分岐がある方が早いのは意外。

[code lang="C"]
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]);
}
[/code]

任意の比較

単純に32bit単位要素比較するか、xorして0比較するか。
うちの環境では下記の3番目が良さそう。

[code lang="C"]
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]);
}
[/code]


前の記事