ちょっとTea Time!?  switch文 vs if文 ? 2020.5.27

 Integration Unitを検討している中で、小容量のPICも接続できるようにプログラムを作成しているが、
一番問題なのは、やはり容量です。18Pinの小さいPICのPIC16F1827では4kWしかありません。それでも、
最初の頃につかっていたPIC16F819(2kW)に比べれば倍はあるわけですが、28PinのPIC16F1938(16kW)や
PIC18F26K20(32kW)に比べるとかなり小さいです。
 容量に余裕のあるPICだと、プログラムをさほど気にせず(いや、気にしろよ!)、だらだらとまでは行きませんが(笑、
書くことができます。反面、小容量のものだとできるだけ短くなるように、冗長な部分があったら1つのサブルーチンにまとめたり
と気を使います。でも、コードを短くすることに気を使いすぎて、処理時間があまりにも長くなるようだと困ります。
そこで、前から気になっていたのですがswitch文とif文ではどちらが早いのか?というのを調べてみることに。
以前、どこかの本ではswitchの方が処理が長いと見たことがあるのですが、自分のつかっているコンパイラ(CCS社)
で調べてみることにしました。

比較してみたのは、下記のような例です。
まず生成されるコードについては若干switch文の方が長いです。でも、あまり大きな差ではありません。
反面、実行速度はswitch文の方が予想に反して早いです。
この原因はアセンブラをみたら明らかでした。
if文では1つの文を淡々とこなしていますが(当然のことながら、そのように書いている)、switch文では
最初に条件分岐のルーチンがあり、サブルーチンコールは別に分けられています。すなわち、一文をすべて
終わるまで次にいけないif文にくらべると、分岐処理だけをちゃっちゃとこなすswitchの方が早いということです。

なるほど、これならSwitch文はプログラムとしても見やすいですから、どんどん使えばいいですね。

switch文の例 if文の例
sub(int i){}

test(int i){
 switch(i){
 case 0:sub(i);break;
 case 1:sub(i);break;
 case 2:sub(i);break;
 case 3:sub(i);break;
 case 4:sub(i);break;
 case 5:sub(i);break;
 case 6:sub(i);break;
 case 7:sub(i);break;
 case 8:sub(i);break;
 case 9:sub(i);break;
 case 10:sub(i);break;
 default:break;
 }


main()
{
 test(10);
}

sub(int i){}


test(int i){
 if(i==0) sub(i);
if(i==1) sub(i);
if(i==2) sub(i);
if(i==3) sub(i);
if(i==4) sub(i);
if(i==5) sub(i);
if(i==6) sub(i);
if(i==7) sub(i);
if(i==8) sub(i);
if(i==9) sub(i);
if(i==10) sub(i);
}


main()
{
 test(10);
}
コード(test関数のみ)
83バイト
コード(test関数のみ)
78バイト
実行時間(64MHz)
約2.6us
実行時間(64MHz)
約3.5us

むしろwrite_eeprom() が問題だった

パラメータを変更したら、それをEEPROMに記憶する関数としてCCSにはwrite_eeprom(int adrs, data) が準備されています。
最初は、単なるサブルーチンコールで実体はライブラリにあるかと思っていましたが、実際にはそうではなく、色々な処理が
インラインに展開されていることがわかりました。そのため、write_eepromの関数を使うと1回あたり42バイト(PIC18Fの場合)も
消費することが判明。こりゃデカイ!実際にアセンブラをみてみると、なにやら色々な処理がインライン展開されています。

中身まではよくわかりませんが・・・

write_eeprom(int adrs,data) test(int adrs,data)
コード長
(PIC18F)
42バイト 10バイト

といことがわかったので、write_eeprom関数については別の名前のサブルーチンで置き換えて、そこから_write_eepromを
呼び出すようにすればwrite_eeprom関数の記述はプログラムの中で1個だけになりますから、相当のコードの節約になります。
なんせ、いまままでwrite_eeprom関数はいたるところにありますから。同様にread_eeprom関数も22バイト消費することが
わかりました。この関数はあまり多くつかいませんが、それでもwrite_eepromと同様の処理をすればプログラムの節約に
なりそうです。

昔のプログラムは凄かったんだな〜と回顧
その昔、CP/M-80をつかっていたことに大学の演習課題もあり、FORTRANを使ったこともありますが、
当時のコンパイラとかってものすごくコンパクトだったな〜と感慨深くなります。なんせコンパイラが
28kバイトしかありません。もちろん、アセンブラやリンカー、ライブラリーは別ですが、それでも総計100kバイト程度
です。いまWINDOWSでつかっているCCS社のCコンパイラはコマンドラインの軽いものですが、それでも15Mバイトあります。
昔のコンパイラに比べると約500倍の容量です。まあ、今と昔ではPCのメモリー容量やCPU速度も1000倍以上の差はあるでしょうから
いいのかもしれません・・・・。コンピュータの資源の無駄遣いのような気もしますが、人間という資源を節約する(開発を効率化する)
という点では合理的なのでしょう。


CP/M-80のコンパイラは軽かったです。

(おしまい)