NetWalker PC-Z1 Cortex-A8 (ARM) 浮動小数演算の実行速度

先日のテストでは Atom に匹敵する実行速度を出していながら、浮動小数演算では
大きく差をつけられる結果となりました。
もう少し詳しく調べたところ、いろいろわかってきました。

結論は
・VFP が遅い
・NEON の SIMD 2/4 を使えばかなり速い
・NEON の単精度スカラー同士の演算は VFP なので遅い

今回は直接命令毎の実行速度を測ってみます。例えば整数乗算なら下記のような感じで。
省略していますが実際には mul 命令を 40 個並べています。
これを 100M 回 (1億) ループします。

static void Start_ASMINT_MUL()
{
    TimerClass  timer;
    timer.Begin();

    __asm__ __volatile__( "\
        mov  r2, #123  \n\
    " : : : "r2","cc" );

    for( int i= 0 ; i< VECTOR_LOOP ; i++ ){
        __asm__ __volatile__(
        "\
            mul	r3, r2, r2 \n\
            mul	r3, r2, r2 \n\
              ~
        " : : : "r3","cc" );
    }

    timer.End( "ASMINT_MUL" );
}

各種演算命令を並べてテストした結果が下記の通りです。
4000M 命令の実行時間の実測値 (秒) です。

i.MX515 Cortex-A8 (ARM) 800MHz
命令                 実行時間        演算ユニットと演算単位
---------------------------------------------------------------
mul  r,r,r           10.21 sec       ALU   32bit
add  r,r,r            5.16 sec       ALU   32bit
fmuls  s,s,s         50.45 sec       VFP   32bit
fadds  s,s,s         45.45 sec       VFP   32bit
fmuld  d,d,d         55.49 sec       VFP   64bit
vmul.f32  s,s,s      50.45 sec       VFP   32bit
vmul.f32  q,q,q      10.07 sec       NEON 128bit (32bit x4)
vadd.f32  q,q,q      10.08 sec       NEON 128bit (32bit x4)
vmla.f32  q,q,q      10.09 sec       NEON 128bit (32bit x4) 積和
vmul.f32  d,d,d       5.04 sec       NEON  64bit (32bit x2)

命令はパイプラインがストールしないよう、意味がないけど依存が発生しない組み合わせで
並べています。

整数演算は mul でおよそ 392M命令/sec です。add の場合は 775M 命令なので、
ほぼ 1命令/cycle に近い数値で実行出来ていることがわかります。

問題の VFP による浮動小数演算は乗算で 5倍、add 比で 9倍も遅くなっています。
倍精度の乗算でも 10% ほどしか増えていないので、オーバーヘッドは別のところに
あるのかもしれません。
丸めモードや Flush-to-Zero モードなど、FPSCR も書き換えてみましたが結果は同じでした。
なお乗算する値が 0 の場合のみ、fmuls は 3割ほど速い速度で完了します。

対照的に NEON がかなり高速です。4要素(128bit) の SIMD 演算であるにもかかわらず
整数演算と同速度で実行しており、2要素(64bit) の SIMD の場合はむしろ整数演算よりも
高速です。ほぼ 1cycle 毎に 2 float の演算をしています。
さらに積和演算が可能なので、SIMD で vmla を用いた場合

40命令 × 100Mループ × 4 (SIMD) × 2 (積和) / 10sec = およそ 3.2G FLOPS

となります。

ではなぜ普通にコンパイルしたプログラムの浮動小数演算が遅いのかと言えば、NEON の
スカラー演算命令は VFP と共有されているからです。
sレジスタ を用いた演算は、NEON ではなく VFP で実行されます。
実際に vmul.f32 s,s,s は fmuls s,s,s と全く同じバイトコードでした。
コンパイラは一般の浮動小数演算では VFP 命令を出力しています。

結局最初に書いたとおり VFP が非常に遅いという結論になります。
そのためスカラー演算であっても 2/4 ベクタとみなして NEON 命令を使った方が
高速になるわけです。
例えば fmuls s,s,s → vmul.f32 d,d,d なら 10 倍です。

実際のアプリケーションで使ったわけではありませんが、周辺を考えなければ Atom 比でも
結構良い結果になるのではないでしょうか。
これが iPod touch 3G/iPhone 3GS の大きさにも収まっていると考えればなおさらです。

vmla は破壊代入するため、他の命令のように同じオペランドのまま並べると依存が発生して
ストールします。5倍ほど遅くなるので、このテストでは代入側レジスタを 5個用いて
インターリーブしています。quad 時スループット 2 のレイテンシ 10 くらいでしょうか。

関連エントリ
NetWalker PC-Z1 Atom と速度比較
NetWalker PC-Z1 Bluetooth とキーカスタムその他
NetWalker PC-Z1 意外にいける
Netwalker PC-Z1 買いました
タッチタイプの境界
NetWalker PC-Z1