Parallella Epiphany optimization tips


漢なら Parallella Epiphany の性能を最大限引き出したいですよね!

![parallella_gui.JPG](https://qiita-image-store.s3.amazonaws.com/0/36275/2b514a33-59a1-9a37-b508-6847cb82d210.jpeg "parallella_gui.JPG")

ここでは Parallella の計算コア(epiphany)を最大限活用する tips を紹介しましょう.

## double

Parallella は float(32bit) の HW サポートにとどまります. double は使えますが SW エミュレーションになります.

**従って double のコードはとても遅いです!**

たとえば以下のようなコードだと,

```
float f = ...
int i = (int)(f * 255.5);
```

```
float f = ...
int i = (int)(f * 255.5f);
```

float なコードと思わせておきながら, 前者の 255.5 はコンパイラで double の定数として解釈されるため, 後者の 100 倍くらい遅くなります(体感速度です. 遅さの解釈には個人差があります).

浮動小数点定数には **f を付けるのを忘れないようにする** か, コンパイラオプションで `-fsingle-precision-constant` を指定して, 定数は float 精度と解釈させるようにします.

## 除算

epiphany core には HW 除算はありません. 近似関数を使いましょう. たとえば

```
// Assume 32bit environment.
inline float fast_sign(float f) {
  float r = 1.0f;
  *((int *)(&r)) |= (*(int *)(&f) & 0x80000000); // mask sign bit in f, set it
                                                 // in r if necessary
  return r;
}

static inline float fast_inv_sqrt(float a) {
  union fi32_u {
    float f;
    int32_t i;
  };
  union fi32_u x;
  x.f = a;
  x.i = 0x5f3759df - (x.i >> 1);
  x.f = x.f * (1.5f - x.f * x.f * 0.5f * a);
  x.f = x.f * (1.5f - x.f * x.f * 0.5f * a);
  return x.f;
}

static inline float fast_inv(float a) {
  float b = fast_inv_sqrt(fast_fabs(a));
  return fast_sign(a) * b * b;
}
```

## math functions

通常の数学ライブラリは使えますが, ソフトウェアエミュレーションなので, sqrtf など float 版を使ってもとても遅いです!  fastmath.h というヘッダが用意されていますが, どうも中身は普通の数学関数のようで, 使ってみても遅いです. 

それぞれ自分で必要なぶんだけ近似関数を実装して使いましょう(全部を実装するとコアのメモリに入りきらない可能性が出てきます).

https://github.com/parallella/parallella-examples

の blobuska や aobench が参考になるでしょう.

## fma

epiphany core は FMA(Fused multiply add)がサポートされていますので, 近似関数でもうまく精度をたもって数学関数など実装できるかもしれませんね. 

http://lipforge.ens-lyon.fr/www/crlibm/

ただし IEEE-754 を全てサポートしているわけではないので注意してください(たとえば丸め).

## 比較命令とか, 丸めとか.

Epiphany では比較命令はありません. fmul などの命令が実行されると, いくつかの結果(結果が zero か, 負数か, など)がステータスレジスタにフラグとしてアップデートされ, jump や conditional move ではそのフラグを見て処理をする, という形になります.

たとえば

```
float mymax(float x, float y) {
        return (x > y) ? x : y;
}
```

というコードは,

```
_mymax:
        fsub r2,r1,r0
        movblt r1,r0
        mov r0,r1
        rts
```

というアセンブラに(理想では)なります.

しかし, この方式では IEEE 754 仕様に準拠できないため(e.g. NaN の扱い), e-gcc の標準では IEEE 754 仕様に準拠するためにソフトウェア処理での比較関数が呼ばれてします...

```
$ e-gcc -O3 -S mymax.c
$ cat mymax.s

_mymax:
        mov r2, %low(___gtesf2)
        movt r2, %high(___gtesf2)
        str lr,[sp],#-2
        jalr r2
        movgt r1,r0
        mov r0,r1
        ldr lr,[sp,#2]
        add sp,sp,#8
        rts
```

`jalr` がそれになります(__gtesf2 というソフトウェア比較関数のアドレスに飛んでいる)

従って, IEEE 754 に忠実でなくてよいのであれば, `-mno-soft-cmpsf` を指定してソフトウェア比較関数を呼ばないように指示します.

```
$ e-gcc -O3 -S -mno-soft-cmpsf mymax.c
$ cat mymax.s

_mymax:
	mov r3, %low(#-917506)
	movt r3, %high(#-917506)
	movfs r2,config
	gid
	movfs ip,config
	and ip,ip,r3
	movts config,ip
	gie
	fsub ip,r1,r0
	gid
	movfs ip,config
	eor ip,ip,r2
	and ip,ip,r3
	eor ip,ip,r2
	movts config,ip
	gie
	movblt r1,r0
	mov r0,r1
	rts
```

**おおっと!** ソフトウェア比較命令へのジャンプが無くなった変わりに, gie/gid(割り込み enable/disable)と config レジスタの r/w 命令が発生していますね...

これは丸めモードの切り替え用のためのようです. Epiphany では丸めモードを config レジスタで設定するため, 一旦割り込みを disable する必要があります.

Epiphany では丸めのデフォルトは round-nearest ですが, C 言語で float -> int 変換は truncate になるのでいずれにせよ truncate モードにしておくのが性能の面で都合がよいです. そのかわり truncate だと演算をするたびに精度がどんどん落ちていくので, 長い依存のある fp 演算のあるコードに対しては適用は避けるようにしましょう. 

`-mfp-mode=truncate` で, 丸めモードを truncate に設定できます.

```
$ e-gcc -O3 -S -mno-soft-cmpsf -mfp-mode=truncate mymax.c
$ cat mymax.s
_mymax:
        fsub r2,r1,r0
        movblt r1,r0
        mov r0,r1
        rts
```

Cool! 念願の最適なコードを手にいれました.

e-gcc の Epiphany 用コンパイルオプションはこちらを参照ください : https://gcc.gnu.org/onlinedocs/gcc-4.7.4/gcc/Adapteva-Epiphany-Options.html