FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

フルスクラッチから作るニューラルネットワーク(10. 性能解析, ARM, RISC-V)

前回、誤差逆伝搬法をlibfixmathライブラリを使って固定小数点化した。しかし逆に固定小数点化により性能が低下してしまっている。 やはりライブラリによるオーバヘッドではないだろうかということで、性能解析を行うことにした。

Google PerfによるPerformance解析

まず、MNISTトレーニングプログラムをGoogle Performance Toolsを使って性能要因解析を行った。double版、float版、fix16_t版で比較し、Callee Mapを作成した。

  • fix16_t 版

f:id:msyksphinz:20170707012031p:plain

  • float版

f:id:msyksphinz:20170707012124p:plain

  • double版

f:id:msyksphinz:20170707012141p:plain

fix16_t版では、ライブラリとして呼び出している、fix16_mul, fix16_addが結構な時間を要している。これはどのような実装になっているかというと、ソースコードを読めばわかる。

#ifndef FIXMATH_NO_OVERFLOW
fix16_t fix16_add(fix16_t a, fix16_t b)
{
    // Use unsigned integers because overflow with signed integers is
    // an undefined operation (http://www.airs.com/blog/archives/120).
    uint32_t _a = a, _b = b;
    uint32_t sum = _a + _b;

    // Overflow can only happen if sign of a == sign of b, and then
    // it causes sign of sum != sign of a.
    if (!((_a ^ _b) & 0x80000000) && ((_a ^ sum) & 0x80000000))
        return fix16_overflow;
    
    return sum;
}

ここで気が付いたのだが、FIXMATH_NO_OVERFLOWというマクロが定義されており、このマクロを定義することによってオーバフローを検出しないコードを出力することができる。具体的にはfix16.hに定義されている以下が使用される。

#ifdef FIXMATH_NO_OVERFLOW

static inline fix16_t fix16_add(fix16_t inArg0, fix16_t inArg1) { return (inArg0 + inArg1); }
static inline fix16_t fix16_sub(fix16_t inArg0, fix16_t inArg1) { return (inArg0 - inArg1); }

#else

コチラを使って、コンパイルして性能測定してみよう。とりあえず詳細な性能測定が面倒なので、timeを使って測定する。

まあまあ、それなりに速くなっているが、それでもdouble, float版には追い付いていない。何故じゃ。

  • FIXMATH_NO_OVERFLOW 版
real    0m23.020s
user    0m22.412s
sys     0m0.544s
  • FIXMATH_NO_OVERFLOW 無し版
real    0m48.244s
user    0m48.044s
sys     0m0.124s

| double 版 | 0m6.659s | | float版 | 0m9.847s | | fix16 w/o NO_OVERFLOW | 0m48.244s | | fix16 w/ NO_OVERFLOW | 0m23.020s |

もともとdouble版をベースに作ったということもあり、floatの方が遅いというのは???だが、fix16_t版は足元にも及んでいない。

Raspberry-Pi3で実行

全く関係ないがRaspberry-Pi3でMNISTプログラムをコンパイルして実行した。全く問題なくlibfixmath, train_twolayrnetもコンパイルできた。実行結果も想定通りだ。

f:id:msyksphinz:20170707015112p:plain

RISC-Vで実行 (コンパイル)

TARGETをRISC-Vに変更し、まずはコンパイルしてみた。

diff --git a/libfixmath/Makefile b/libfixmath/Makefile
index b284590..2acfc04 100644
--- a/libfixmath/Makefile
+++ b/libfixmath/Makefile
@@ -3,15 +3,18 @@ PROJECT = libfixmath
 LIB =
 SRC = .
 INC =
+# DEF = -DFIXMATH_NO_OVERFLOW

 #Compiler settings
-CPP = gcc
-CC = gcc
-AS = gcc
-LD = gcc
-AR = ar
-CPP_FLAGS = -O2 $(INC) -Wall -Wextra -c
-CC_FLAGS  = -O2 $(INC) -Wall -Wextra -c
+TARGET =
+
+CPP = $(TARGET)gcc
+CC = $(TARGET)gcc
+AS = $(TARGET)gcc
+LD = $(TARGET)gcc
+AR = $(TARGET)ar
+CPP_FLAGS = -O2 $(INC) $(DEF) -Wall -Wextra -c
+CC_FLAGS  = -O2 $(INC) $(DEF) -Wall -Wextra -c
$ make TARGET=riscv64-unknown-elf-
riscv64-unknown-elf-gcc -O2  -DFIXMATH_NO_OVERFLOW -Wall -Wextra -c -o fix16_str.o fix16_str.c
riscv64-unknown-elf-gcc -O2  -DFIXMATH_NO_OVERFLOW -Wall -Wextra -c -o fract32.o fract32.c
riscv64-unknown-elf-gcc -O2  -DFIXMATH_NO_OVERFLOW -Wall -Wextra -c -o uint32.o uint32.c
riscv64-unknown-elf-gcc -O2  -DFIXMATH_NO_OVERFLOW -Wall -Wextra -c -o fix16_exp.o fix16_exp.c
riscv64-unknown-elf-gcc -O2  -DFIXMATH_NO_OVERFLOW -Wall -Wextra -c -o fix16_sqrt.o fix16_sqrt.c
riscv64-unknown-elf-gcc -O2  -DFIXMATH_NO_OVERFLOW -Wall -Wextra -c -o fix16_trig.o fix16_trig.c
fix16_trig.c: In function 'fix16_tan':
fix16_trig.c:116:9: warning: implicit declaration of function 'fix16_sdiv' [-Wimplicit-function-declaration]
  return fix16_sdiv(fix16_sin(inAngle), fix16_cos(inAngle));
         ^~~~~~~~~~
riscv64-unknown-elf-gcc -O2  -DFIXMATH_NO_OVERFLOW -Wall -Wextra -c -o fix16.o fix16.c
riscv64-unknown-elf-ar rcs libfixmath.a   ./fix16_str.o  ./fract32.o  ./uint32.o  ./fix16_exp.o  ./fix16_sqrt.o  ./fix16_trig.o  ./fix16.o

train_twolayernetの方も、CC=riscv64-unknown-elfを設定してコンパイルする。

$ make CC=riscv64-unknown-elf-gcc train_twolayernet_fix16
riscv64-unknown-elf-gcc  -std=gnu11 -O3 -g -DFIXMATH_NO_OVERFLOW train_twolayernet_fix16.c -o train_twolayernet_fix16 -L./libfixmath/libfixmath -I./libfixmath/libfixmath -lfixmath -lm