2013年06月27日

ARMのNEONのSIMD命令をgccのオートベクタライズの最適化で使う方法

以前にgccでNEONのSIMD命令を生成させる方法という記事を書きましたが、今はかなり状況が変わっています。コンパイラのオートベクタライズの最適化はずっと進化していて、簡単なコンパイルオプションをつけるだけでNEONのSIMD命令を活用することができるようになっています。



Ubuntu 12.04LTSのarm-linux-gnueabihf-gccを使用しています。

$ arm-linux-gnueabihf-gcc -v
Using built-in specs.
COLLECT_GCC=arm-linux-gnueabihf-gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/arm-linux-gnueabihf/4.6/lto-wrapper
Target: arm-linux-gnueabihf
Configured with: ../src/configure -v --with-pkgversion='Ubuntu/Linaro 4.6.3-1ubuntu5' --with-bugurl=file:///usr/share/doc/gcc-4.6/README.Bugs --enable-languages=c,c++,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.6 --enable-shared --enable-linker-build-id --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/arm-linux-gnueabihf/include/c++/4.6.3 --libdir=/usr/lib --enable-nls --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object --enable-plugin --enable-objc-gc --enable-multilib --disable-sjlj-exceptions --with-arch=armv7-a --with-float=hard --with-fpu=vfpv3-d16 --with-mode=thumb --disable-werror --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=arm-linux-gnueabihf --program-prefix=arm-linux-gnueabihf- --includedir=/usr/arm-linux-gnueabihf/include --with-headers=/usr/arm-linux-gnueabihf/include --with-libs=/usr/arm-linux-gnueabihf/lib
Thread model: posix
gcc version 4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5) 

これより新しいgcc 4.7, 4.8を使っても同様です。

整数演算のベクタライズ

以下の関数はint配列の要素の相加平均を求めるものです。こんな単純な関数でもベクタライズの最適化を適用できます。

int int_average(int* array, int size)
{
	int i;
	long long total = 0;

	if (size <= 0) {
		return 0;
	}
	for (i = 0; i < size; i++) {
		total += array[i];
	}
	return total / size;
}

最適化オプション -O2の場合

$ arm-linux-gnueabihf-gcc -mfpu=neon -O2 -S int_average.c 
int_average:
	@ args = 0, pretend = 0, frame = 0
	@ frame_needed = 0, uses_anonymous_args = 0
	push	{r3, r4, r5, lr}
	subs	r3, r1, #0
	ble	.L4
	subs	r2, r0, #4
	movs	r1, #0
	movs	r0, #0
	movs	r4, #0
.L3:
	ldr	r5, [r2, #4]!
	adds	r4, r4, #1
	adds	r0, r0, r5
	adc	r1, r1, r5, asr #31
	cmp	r4, r3
	bne	.L3
	mov	r2, r4
	asrs	r3, r4, #31
	bl	__aeabi_ldivmod
	pop	{r3, r4, r5, pc}
.L4:
	movs	r0, #0
	pop	{r3, r4, r5, pc}

最適化のオプション -O3 をつけるとベクタライズを行います。

$ arm-linux-gnueabihf-gcc -mfpu=neon -O3 -S int_average.c 
        ...
	vmov.i32	q9, #0  @ v2di
	add	r2, r0, r2, lsl #2
	movs	r3, #0
.L7:
	vldmia	r2!, {d16-d17}
	vmovl.s32 q10, d16
	adds	r3, r3, #1
	vmovl.s32 q8, d17
	vadd.i64	q9, q10, q9
	cmp	r3, ip
	vadd.i64	q9, q8, q9
	bcc	.L7
        ...

SIMD命令が使われています。

得られた結果は以下に貼りました。

https://gist.github.com/tetsu-koba/5873910

浮動小数点演算のベクタライズ

float float_average(float* array, int size)
{
	int i;
	float total = 0;

	if (size <= 0) {
		return 0;
	}
	for (i = 0; i < size; i++) {
		total += array[i];
	}
	return total / size;
}

先ほどと同様の相加平均を求める関数のfloat版です。

arm-linux-gnueabihf-gcc -mfpu=neon -O3 -S float_average.c 
float_average:
	@ args = 0, pretend = 0, frame = 0
	@ frame_needed = 0, uses_anonymous_args = 0
	@ link register save eliminated.
	cmp	r1, #0
	ble	.L4
	subs	r0, r0, #4
	movs	r3, #0
	flds	s15, .L7
.L3:
	adds	r3, r3, #1
	adds	r0, r0, #4
	cmp	r3, r1
	flds	s14, [r0, #0]
	fadds	s15, s15, s14
	bne	.L3
	fmsr	s13, r3	@ int
	fsitos	s14, s13
	fdivs	s0, s15, s14
	bx	lr
.L4:
	flds	s0, .L7
	bx	lr

x86_64では-O3で浮動小数点演算もベクタライズされますが、ARMではそうなりません。その理由はNEONの浮動小数点演算はIEEE754標準の全てを満たしていないためです。 -funsafe-math-optimizations のコンパイルオプションを追加するとベクタライズされるようになります。

ここではもっと簡単に -Ofast をつけることにします。これは「厳密さよりも速度優先」です。 -O3 の最適化に追加して厳密さを欠く最適化も行います。グラフィックスの座標計算のような用途ではこちらが適しています。

arm-linux-gnueabihf-gcc -mfpu=neon -Ofast -S float_average.c
        ...
.L7:
	adds	r2, r2, #1
	vldmia	r4!, {d18-d19}
	cmp	r2, r5
	vadd.f32	q8, q8, q9
	bcc	.L7
        ...

SIMD命令が使われています。

得られた結果は以下に貼りました。

https://gist.github.com/tetsu-koba/5873935

まとめ

ARM Neonでベクタライズの最適化をして欲しいときには -Ofast をつける。整数演算だけでよい場合は -O3でも可。



トラックバックURL

コメント一覧

1. Posted by やまも   2014年01月21日 00:23
5 参考にさせてもらいました。ありがとうございます

コメントする

名前
URL
 
  絵文字
 
 
記事検索
最新コメント
アクセスカウンター
  • 今日:
  • 昨日:
  • 累計:

QRコード
QRコード