新しいコンパイラと最適化設定でまた割り算の最速化を試みる

XC8 コンパイラV3.00リリース

MPLAB XC8 C Compiler v3.00 Release Notes for PIC MCUx

新しいV3.00のコンパイラ(XC8)がリリースされていたので、リリースノート見ていたらよさげな改良が入っていた。いままでミッドレンジのPICは割り込み要因別の関数を作ることはできなかったので、統一した割り込み関数を作っておいて、そこに飛んだあとに割り込みフラグをチェックしてどの要因かを判別する必要があった。これを高級なPICのように、割り込みフラグ別に関数を分けることができるらしい。要は

void __interrupt() InterruptManager() {
    if (CCP1IF) {}

}

と書いていたところ、それぞれの割り込み要因別に独立した関数にできるので、

void __interrupt(__flags(CCP1IF)) InterruptManager() {}

と直接書ける。これで実行時間早くなるよ!みたいなこと書いてあったのでやってみたけど、もともとのコードだと割り込み要因発生からフラグチェックして中身の1行目に到達するまで9カウント、新しい書き方だと12カウント。なんで遅くなるのよ。これ、割り込み要因ごとのif文を全て確認しなくて済むから、後ろのほうでチェックするフラグの割り込みだと遅くなるのが改善するよって言ってるだけか。まあif文でチェックするのに3,4カウント使ってるので、2個目にチェックするフラグ以上は早くなることになる。今んところ1個目チェックがピックアップ第一波で、2個目が点火だから、どっちも早いに越したことない処理ではあるけど、どうしたもんか。ほかの割り込み要因はUART受信とかタイマオーバーフローとかどうでもいい処理だし、早い必要がない。悩みどころだけど、気にするほどの差ではないし、可読性は上がるからいれといてもいいかな。気に入らなければ戻すかも。

コンパイラ最適化設定

あと今まで知らなかったのだけど、FreeのXC8を使っていても、コンパイラの最適化設定を調整できるようになっていた。というわけで早速一番早くしたい割り算のコードを最適化設定を変えて実行してみる。コードは以下の通りで、割られる数は37500の定数である。ついでにコンパイラを今まで使っていたV2.45と、新しいV3.00で違いがあるのか調べてみた。

        t1_count = 4000;
        rpm = (uint8_t) (numerator_rpm / (t1_count >> 4));

XC8 V2.45     XC8 V3.00    
最適化レベル 計算時間 us 改善率 最適化レベル 計算時間 us 改善率
0 52 0% 0 50 0%
1 45.875 12% 1 45.125 10%
2 45.625 12% 2 44.875 10%
3 45.625 12% 3 44.875 10%

結果、そもそもコンパイラが違うだけで2us (8サイクル) も早くなることが分かった。さらに最適化レベルを上げていくと、1でも十分なくらい早くなる。これはいいね。

ついでにプログラムメモリとデータメモリも見てみる。

レベル0 V2.45

レベル1

レベル2

レベル3

V3.00 レベル3

これも少しだけどV3.00のほうが優秀だし、最適化でも1,2%は変わるみたいだ。