2014年6月21日土曜日

XC16でコンパイル最適化を掛ける

さて、ここ暫くの間NTP時計を家で運用していますが、特に大きなバグ等にはぶち当たっていません。めでたく安定動作しております。

ところで、コンパイラにはコンパイル最適化というものがあります。C言語のコードは基本的に人間様への可読性が命です。アルゴリズムとかそういう根本的なところではスピードやコードのコンパクトさを意識する必要はあるかもしれませんが、細かな記法において、わざわざ人間様への可読性を大幅に下げてまでスピードを上げるという考え方には懐疑の念を抱かざるを得ません。もちろん、熟練したプログラマーとして可読性を維持したまま高速・コンパクトな記法をできるのならばそれに越したことはありませんが。

はい、話がずれてきましたが、要は、コンパイラには「人間様が多少非効率なコードを書いたとしても、それをコンパイル時に効率的なコードに変換する機能」が求められます。それが、コンパイル最適化です。

しかし、コンパイル最適化には、特にPICのような組み込みマイコンではしばしば問題が起きます。

例えば、EEPROMアクセス。PIC16シリーズとかPIC18シリーズでは、EEPROMに書き込むときに、書き込みシーケンスとしてEECON2に0x55を書き込んだあとに0xAAを書き込み、そしてEEPROMにデータを書き込むという動作をしなければなりません。これは、偶発的なトラブルで不用意にEEPROMが書き換えられてしまわないように冗長性を持たせるという意味が込めてあります。
このようなクリティカルなコードを実行する場合、C言語の開発であってもインラインアセンブリで書いてしまうことも多いとは思いますが、 仮にC言語で書いたとしましょう。

EECON2 = 0x55;
EECON2 = 0xAA;
EECON1bits.WR = 1;

はい。こんな感じになるかと思います。
これはおそらく最適化を掛けなければ正常に動作するでしょうが、最適化を掛けた場合、コンパイラが「EECON2に0x55を書き込んだ直後に0xAAを書き込んでるし、その間では参照されていないから0x55を書き込む必要なんて無いね」と判断しかねません。もっと言えば、この先でEECON2を読み出すこともおそらく無いでしょうから、0xAAを書き込むコードすらコンパイル時に消されてしまうかもしれません。

これは、ときに非常に難解な問題になります。
どう見てもソースコードには書き込みシーケンスが書いてありますが、コンパイルして出来上がったオブジェクトファイル及びHEXファイルにはこのシーケンスは入っていません。しかし、一般にオブジェクトファイルやHEXファイルは人間様が読めないので、 その問題に気づけることは知らないとなかなか無いでしょう。そして「よくわからないけど動かない」という現象が起こるわけです。

はい。このような問題はしばしば起こります。なので、最適化においてはこのようなことを念頭において置かなければいけません。

では、上記のようなコードを書きたいときはどうするか?
はい。「volatile」という修飾子がC言語には用意されています。 英語の意味は「揮発性の」とか「変わりやすい」とかそういう意味だそうです。これを変数宣言時に付けると、その変数に関わる最適化を抑制することができます。なので、おそらく組み込みマイコンのSFRのアクセスに関してなんかは、ライブラリの中でその変数にvolatileが付けられた状態でtypedefなり何なりがされていることでしょう。


はい、要するに何が言いたいかと言いますと、最適化は「賭け」なんです。
コードの実行速度やコードサイズは小さくなるかもしれませんが、自分が意図しないところまで最適化されてしまうとソフトウェアが上手く動かなくなる可能性があります。特に、単にロジックをコードで表現しただけのようなプログラミングと違ってハードウェアを制御する組み込みプログラミングでは最適化でしばしば起こりそうな問題があるからです。
もちろん、コンパイラの最適化の仕様を熟知していて、さらに他人が作ったライブラリも含めて最適化を掛けられる前提でvolatileなどの修飾子を適切に付けてあるコードだったら問題は無いのでしょうが、そこまでやるのはなかなか難しいものです。

でもまあとりあえずやってみました。上手く動かなかったら最適化をやめてプログラムを書き直せばいいかなって。


最適化をやるのは簡単です。プロジェクトのプロパティからxc16-gccを選び、option categoriesをOptimizationsにします。そして、Optimization levelを設定すればいいだけです。細かな説明はOption Descriptionのタブに書いてくれています。
上位の最適化は有料版XC16などでしか有効にならないので、とりあえず無料版でできる最適化レベル1にしてコンパイルしてみましょう。


こちらが最適化レベル0(最適化なし)のときのコードサイズです。プログラムメモリは85%も使っています。


そして、最適化レベルを1にしたらこうなりました。見事に66%までコードサイズが減っています。

さて、問題の最適化の副作用ですが…今のところ特に無さそうですね。
まあ、様子見です。

0 件のコメント:

コメントを投稿