PICのコードサイズ最適化について - d
2016/08/31 (Wed) 17:54:16
PICのプログラムサイズが8kという話を聞いて、けっこう多いなぁと思いました。
(私は途中までしか組んでいないからわからないのです。そのくらいかかるのかもしれません。)
現在私もCS8416とPCM1792Aの組み合わせで製作中です。
赤外線モジュールを使って、リモコンを実装しようと思っています。
ゆくゆくはUSBを使ってWin上からコントロールします。
さて、プログラムサイズですが、何か最適化をされていますか?
インターネット上にはほとんど載っていませんが
コンパイラの最適化以外にも最適化の手法はあります。
(コンパイラが賢くて実現してるかもしれませんけど)
割り算
普通は割り算ごとに割り算マクロが展開されるはずです。
ためしに割り算を複数実装してみてください。
1つ実装するごとに大幅にコードサイズが増えるようなら
割り算は関数にして、実行のたびに呼び出したほうがコードは小さくてすみます。
(ちなみにコードサイズがまったく増えない場合、コンパイラが使わない変数を削除し、計算していない可能性もあります。)
除数、被除数ともに変数になるので、オーバーヘッドが少し増える可能性がありますが
わずかだと思います。
引き算に変える手もあります。10で割るんなら10で何回か引き、カウンタをインクリメントしてやるだけです。
ただ、被除数が大きくなると、計算時間がものすごく増えます。
私はBYTEデータ(Max 255)の最上位桁をdata/10から10を数回引くという引き算で計算しています。最大でも2回しか引きません。
1の位は後述の方法で求め、割り算は10で割る1回だけです。
掛け算
PICでは掛け算もできません(新しいのは不明)。しかもビットシフトも1ビットシフトしかできません。
このことを利用して最適化を行います。コンパイラによっては最適化されてるのかも。
よく使うのは*10,*100の高速化です。
どうやるのかというと、最小のビットシフトの和に変形するだけです。
A * 10 は A * 8 + A * 2です。でもまだです。(A*4+A)*2がもっとも高速です。
ただ、もともと最適化できるコンパイラの場合、レジスタの割付けなどで逆に遅くなる可能性もあります。
そこのところは生成コードサイズから推測してください。
ちなみに、10で割った余りを計算するのは時間がかかるので1の位はData - Div_10 * 10で求めてます。
ビットシフト
*4と<<2を両方試してみてください。コンパイラによりますが、同じコードが生成されるはずです。
/32も同様です。2のべき乗の演算は全部シフト演算に置き換えられます。
シフト演算は見づらいのと、優先順位が低いので()で囲まないと思わぬ計算間違いのもとです。
XC8では最適化がかかるようなので、私はシフト演算子はほとんど使いません。(signedは別です。)
私のLRCK測定ルーチンは80ms計って8で割るんですが100ms計って10で割るよりよっぽど高速です。
44100のうち、下2桁を丸めて捨てます。10msにしないのは測定誤差を丸めで消すためです。
ポインタ、関数ポインタ
使い方によっては、単に見づらくなりエラーの元にしかなりませんが、うまく使うと
コードを見やすくし、処理効率があがります。関数ポインタのテーブルは分岐を減らし、見やすくなって便利です。
変数サイズ
これは簡単ですね。最小のサイズがいいです。できるだけunsignedを使うのもポイントです。
実行アルゴリズム
読みやすくて速くて軽量な処理ルーチンを考え出すことこそ、プログラムの最大の難関であり、醍醐味であり、
腕の見せ所です。
あとは、出力されるコードサイズを見ながら適宜編集でやっています。
私のは現在のコードサイズが2kちょいで、あとI2CでのICコントロールが必要といったところです。
液晶I2Cと、赤外線は実装しました。
全部で4kくらいかなぁと思ってますが・・・まあわかりませんね。
私のは赤外線部分があるのと、コントロールはロータリーエンコーダを使うんですが、液晶はI2Cです。
CS8416とPCM1792Aの組みあわせはレジスタの設定もそんなに多くありません。
ヒロさんのは自前でI2Cを実装し、液晶はI2Cでないやつを使ってるのは大きいかもしれませんね。
知っている範囲でしたらすみません。
Re: PICのコードサイズ最適化について - d
2016/08/31 (Wed) 18:11:00
まだありました。
constと#defineでは#defineはコンパイル前に置き換えるだけなので一般に高速です。
また、定数演算は事前に実行されるため、計算時間は事実上必要ありません。
ただし、演算は大体において左から実行されるため、
*14*3とか書いておくと意外と2回計算されます。
(14*3)と()でくくるのがよいです。
しかし、割り算を含む場合、桁落ちが発生して思わぬ計算ミスにつながる可能性もありますので注意してください。オーバーフローにも注意です。
また思いついたら書くかもしれません。
Re: PICのコードサイズ最適化について - ジョン スミス
2016/09/02 (Fri) 08:30:43
d 様
今度(後) 掲示板にご記入の時は、
........ここから..........
xxxの機能追加版を同じPICで、作りました。
xxxxx.comにソースファイルとHEXファイルを公開しています。
ご自由にお使いください。PICを書き込めない方は、ご連絡ください。
........ココまで..........
たった3行で、皆さんに喜ばれると思います。
Re: PICのコードサイズ最適化について - d
2016/09/02 (Fri) 20:11:02
まずは、不快に思わせてしまったのならすみません。
お詫びします。
これは単なる情報提供として書いたつもりだったので、残念です。
知識をひけらかしているつもりはなく、ちゃんと実証用のコードも
書いているつもりだったのですが。
わかりにくかったらもっと具体的に書きましょう。
割り算については
WORD Div(WORD dividend, BYTE divisor){
return dividend / divisor;
}
こうですね。
掛け算についてはあまり再掲とはいえないかもしれませんが
(A*4+A)*2
ですね。
ビットシフトは単に書き方の問題です。
Defineとconstの話は、一般的に文字列がconst、数値がdefineです。
あと、ソースコードは申し訳ありませんが公開できません。
ソースコードは労力と知識の結晶であり、タダでは公開できるようなものではないのです。
ヒロさんがソースコードを公開されていないのも、タダでは割りにあわないと思ってらっしゃるからだと思います。
Re: PICのコードサイズ最適化について - d
2016/09/03 (Sat) 00:55:15
ジョン スミスさん、ありがとうございました。
Re: PICのコードサイズ最適化について - ヒロ@出先
2016/09/04 (Sun) 09:32:23
私の場合ですが
1.コード最適化
これについてはコンパイラまかせです。PICの容量にも余裕があるので、冗長なコードを書きまくっているかもです。それでも、どうしても容量に余裕がなくなってきたら、機能が重なっているルーチンを一つにまとめたりする程度で、こまかいところまでは手をつけないのが現状です。
2.速度最適化
これについても、あまり考えていないというのも現実でしょうか。PICの速度も十分なのでそれほど気にしていないです。ただ現在取り組んでいるデータロガーが極力冗長な部分を省いて高速化を図っています。現在は1chサンプルだと最大45kHz?sampleまではいけそうです。
補足
大昔ですがコンパイラの最適化オプションを変更すると(最適化を強めると)、答えが異なるという現象もありました。そのため、つねに違うEWSで答えが一致することを確認してから動かしていた覚えがあります。まあ、あのころは機種が変わると答えが違うということもありましたが・・・。さすがに、今のコンピュータでは問題ないでしょうが。