Fuzix-Compiler-Kitの6800用コンパイラ(9) peep hole optimizer(2)
EtchedPixels/Fuzix-Compiler-Kit: Fuzix C Compiler ProjectのCコンパイラ付属のpeephole optimizer (のぞき穴的最適化プログラム)の話の続き。
簡単な最適化でどれぐらい速くなるか、確認してみよう。
テストプログラムその1
ASCII誌に掲載されていたベンチマークテストNo.7をCに書き換えたもの。元々はkilobaud誌に掲載されていたものです。元々がBASICなのでgotoでloopを回しています。
cpu_counter() は、それまでに実行したCPUサイクルを表示する関数。common.h で定義しています。
#include "common.h" void sub() { } int main(int argc, char *argv[]) { int k=0; int l; int a; int m[6]; cpu_counter(); L: k=k+1; a=k/2*3+4-5; sub(); for(l=1; l<=5; l++){ m[l]=0; } if(k<1000) goto L; cpu_counter(); return 0; }
これを現時点(2024/12/17)のFuzix C Compuler 6800用で走らせてみます。
本体部分の実行サイクルは 1,327,055 で、1MHzの6800だと1.3秒、ベーシックマスターだと1.8秒ぐらいかかります。
../emu6800 6800 9997-bench7 9997-bench7.map CPU cycles = 154 CPU cycles = 1327209 0
プログラムを見ればわかるように、時間がかかりそうなのは二重ループの内側で、外が1000回・中が5回ループするので5000回実行されます。
ここのオブジェクトコードを見てみましょう。 m[l]=0; の行。
素直に l を2倍して、スタックポインタの値と足して、配列要素の値を求めています。冗長ですが、6800だとどうしてもこうなってしまう。最適化の余地はあまりなさそうです。
(ループ変数 l が 1-5 までなので、ldb 3,x / lslb だけにする手はありますが、peephole optimizerの
tsx ldb 3,x lda 2,x lslb rola sts @tmp addb @tmp+1 adca @tmp addb #7 adca #0 staa @tmp stab @tmp+1 ldx @tmp clr 1,x clr 0,x
ループ判定を見てみます。for(l=1; l<=5; l++){ の l<=5 の部分。 l と 5を比較するのに、l をスタックに積んで 5 をAccABに入れて、サブルーチンを呼んでいます。直接比較して分岐すれば速くなりそうです。
tsx ldb 3,x lda 2,x pshb psha clra ldab #5 jsr __cclteq ; jeq L31_b jmp L31_n
rules.6800に下記のコードを追加します。分岐ラベルを %2_%3 の形にしているのは、関係演算の結果を0/1にしない場合だけを最適化したいからです。jeq %2 とするとバグります。
pshb psha clra ldab %1 jsr __cclteq ; jeq %2_%3 = subb %1 sbca #0 jgt %2_%3 jlt M%M_cc ; jsr __cclteq tstb jne %2_%3 M%M_cc:
再度 実行。867055サイクルになりました。元の65%。ずいぶん速くなります。
../emu6800 6800 9997-bench7 9997-bench7.map CPU cycles = 154 CPU cycles = 867209 0
peephole optimizeは、使いすぎるとデバッグがしづらくなりますし、コンパイラが出力するコードが変わったらやり直しです。
どうしても速くしたい・短くしたいプログラムがあるときだけ使いましょう(そもそも、その場合は6800用にはしないか…)
ディスカッション
コメント一覧
まだ、コメントがありません