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

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

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




比較演算

素直に比較すると要素ごとの比較になり、意図した結果にならない。
(そもそも結果は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にも記事があった。

上の記事は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命令って高速なんでしょうか?

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]);
}