mbed で NEON を使ってみる
NEONって使ったことないんですが、GR-PEACHのCPUはARM-CortexA9なので、NEON命令が使えるはずです。mbedのオンラインコンパイラ(ARMCC)では使えるのでしょうか。試してみました。
とりあえず下の配列の合算プログラムをビルド。すんなり通りました。"arm_neon.h"も見えてるし、NEON関数書けばそのまま使えるようです。
合算プログラムは、255までの整数が入ったuint16_tの配列を用意して、その合計を求めます。sum_loop()では単純に配列をループで回して合算し、sum_neon()では、ループでNEON関数をつかって、uint16_t 4列のベクタに配列を読んで、uint32_t 4列のベクタに合算していきます。したがって、ループ回数はsum_loop()の 1/4 になります。最後に、対加算という関数で列同士の合算を行って返しています。
#include <stdio.h> #include <arm_neon.h> #include "mbed.h" Serial pc(USBTX, USBRX); Timer tim; void fill_array(uint16_t *array, int size) { for (int i = 0; i < size; i++) { array[i] = rand() % 255 + 1; } } int sum_neon(uint16_t *array, int size) { uint32x4_t acc1 = vdupq_n_u32(0); uint64x2_t ret; for (; size != 0; size -= 4) { uint16x4_t vec = vld1_u16(array); array += 4; acc1 = vaddw_u16(acc1, vec); } ret = vpaddlq_u32(acc1); return (int)(vgetq_lane_u64(ret, 0) + vgetq_lane_u64(ret, 1)); } int sum_loop(uint16_t *array, int size) { int ret = 0; for (int i = 0; i < size; i++) { ret += array[i]; } return ret; } void test(int size) { int s1, s2, e1, e2, sum1, sum2; uint16_t my_array[size]; printf("sum(%d elements) test ----\n", size); fill_array(my_array, size); s1 = tim.read_us(); sum1 = sum_neon(my_array, size); e1 = tim.read_us(); s2 = tim.read_us(); sum2 = sum_loop(my_array, size); e2 = tim.read_us(); printf(" neon: sum = %d, time = %d [usec]\n", sum1, e1 - s1); printf(" loop: sum = %d, time = %d [usec]\n", sum2, e2 - s2); printf("\n"); } int main() { tim.start(); test(100); test(1000); test(10000); test(100000); tim.stop(); return 0; }
そして結果は下のようになりました。
サイズ100の配列では単純にループした方が早いけど、それ以外はNEON関数を使ったほうが速かったです。配列のサイズで時間差がまちまちなのは何ででしょう。
sum(100 elements) test ---- neon: sum = 13419, time = 6 [usec] loop: sum = 13419, time = 3 [usec] sum(1000 elements) test ---- neon: sum = 128368, time = 3 [usec] loop: sum = 128368, time = 13 [usec] sum(10000 elements) test ---- neon: sum = 1284233, time = 20 [usec] loop: sum = 1284233, time = 119 [usec] sum(100000 elements) test ---- neon: sum = 12859428, time = 470 [usec] loop: sum = 12859428, time = 1595 [usec]
NEON関数を明示的に使ってみましたが、演算結果の桁数まで考慮してNEON関数を使わないといけないので難しい印象です。最適化オプションでNEON命令を使うようにコンパイルできるようなのでそれが良いですね。mbedのオンラインコンパイラでは、コンパイルオプション指定できないので、pragmaで指定できれば良いなと思いました。