E500BASIC高速化のすべて

PART 6 メインルーチンの軽量化


 PART4の(3)について考えていきましょう。
 アルゴリズムの改善以外の方法でメインルーチンを軽量化するには下記の2つの方法があります。

(1)メインルーチン内で必ず必要なもの以外は必要な時に行う
(2)メインルーチン外でできることは前もってやっておく

 まずは(1)から考えてみましょう。

 例えばゲームでスコア表示を行う場合、スコアは大半のゲームでは何らかのリアクションをした時のみに加算されると思います。それならばスコア表示はそのリアクションをした時のみ表示をすればいいのでメインルーチン内で常時PRINT文などを用いて表示する必要がないことが分かりますね。このように常時実行しなくても良いものというのがどういうものかを考えてみることにします。

 PART5で出てきた[1][3]の交互押しプログラムで100回押したら終了するように改良したものが下記のリストです。

リスト1
10 F=1:X=0
20 IF F=VAL INKEY$ LET X=X+1:F=4-F
30 IF X=100 END
40 GOTO 20

 回数が100に達しているかという判定は常に行う必要はなく回数(Xの値)が加算されたときのみ判定すれば良いので次のようになります。

リスト2
10 F=1:X=0
20 IF F=VAL INKEY$ LET X=X+1:F=4-F:IF X=100 END
40 GOTO 20

 これを元に考えるとPART3「VS」においては勝敗判定は常に行う必要がなく「VSターボ」のようにエネルギーが減少した時のみ行えばよいということが分かるでしょう。

 次のリストはゲームなどでよく使われている残りタイムの表示部分のみです。タイム999からスタートしてメインルーチン1回ごとに1ずつ減り、残りタイムが0になったらタイムオーバーになるようになっています。

リスト3
10 T=999
20 LOCATE 0,0:PRINT "TIME";T
30 T=T-1:IF T>=0 THEN 20
40 PRINT "TIME OVER"

このリスト3はメインルーチン内に特に無駄は無さそうですが、実はメインルーチン内で実行する必要の無い部分があります。それは30行目です。

リスト4
10 FOR T=999 TO 0 STEP -1
20 LOCATE 0,0:PRINT "TIME";T
30 NEXT
40 PRINT "TIME OVER"

 このリスト4ではFOR〜NEXTのカウンタをタイム減算処理に使用し、最終値をIF文の代わりに使用しています。このようにFOR〜NEXT使用による高速化は使える場面も多いため積極的に活用していきましょう。

 では、タイム99からスタートし、タイムがメインルーチン10回につき1ずつ減るような場合はどうなるでしょう。これはFOR〜NEXTを使用するならばきざみ値で指定すれば簡単にできますね。

リスト5
10 FOR T=99 TO 0 STEP -0.1
20 LOCATE 0,0:PRINT "TIME";INT T
30 NEXT
40 PRINT "TIME OVER"

 リスト5は特に問題は無さそうですが、メインルーチン10回あたり1回しか表示する必要のない残りタイムを毎回表示するというのは無駄でしかありません。だから、表示を10回に1回しか行わないようにしてみます。

リスト6
10 FOR T=99 TO 0 STEP -0.1
20 IF T=INT T LOCATE 0,0:PRINT "TIME";INT T
30 NEXT
40 PRINT "TIME OVER"

 しかし、リスト6のようにメインルーチン内で常時やるべきかどうかを判定するために別途IF文をプラスすると遅くなる可能性もあります。実は、FOR〜NEXTを使えばIF文を使わずに10回に1回だけ表示というのも簡単にできてしまうのです。

リスト7
10 FOR T=99 TO 0 STEP -1
20 LOCATE 0,0:PRINT "TIME";T
30 FOR U=1 TO 10
40 NEXT :NEXT
50 PRINT "TIME OVER"

 リスト7の30行と40行の間にゲームのメインルーチンを挟めばタイム減算処理および10回に1回だけ行うタイム表示処理というのはメインルーチン内では不要ということが分かると思います。つまり、1タイムあたり10回のIF文が省略できる分だけ高速になるということです。ただ、多重ループを使うとメインルーチンから抜け出す処理で問題が発生することがあります。その際にはFOR〜NEXTのペア変更のテクニックなどを用いると良いでしょう。そのテクニックを使えばメインルーチンで1P用、2P用で判定が必要な場合も2つのFORを使うことでその判定に使っているIF文1つ分だけ高速にすることもできます。

 リスト1もFOR〜NEXTを使えば次のようにできます。

リスト8
10 F=1
20 FOR X=0 TO 100
30 IF F=VAL INKEY$ LET F=4-F:NEXT :END
40 GOTO 30

 あと、ゲームでキャラの移動をするときは元のキャラ消去→新しいキャラ表示というのを繰り返すわけですが、常時移動するキャラでないものに関してはキャラ消去を行う必要はありません。
 また、OPASでは表示キャラの変更は保存されているデータが記録されているアドレスを計算で求めることで実現していますが、そのアドレスを計算しやすい場所にすることでメインルーチン内で上位、中位アドレスの計算をする必要がなくなります。このように常時実行なくて良い処理をメインルーチン内で常時実行していることは少なくないためにそのような処理を減らすだけでもかなりの高速化ができます。

 次に(2)について考えてみましょう。

 ゲーム内で三角関数などの時間のかかる演算を行う場合、メインルーチン内で処理すればその動作速度が気になるところです。しかし、時間のかかる演算も事前に計算可能な類のものであればやっておいて配列変数などに格納するいわゆるテーブル処理を行うことによって高速化が可能になります。
 このテーブル処理においてはどの程度の精度が必要かというのが問題になってきます。三角関数で10度単位で360度分のデータが必要ならば36個の配列変数を確保の必要があり、3度単位必要ならば120個の配列変数が必要になります。
 また、GOTO文をテーブル処理することによってゲーム内で多数に分岐処理しないといけないもの(例えば多くのキーを使ったゲームなど)で高速化が可能になります。

PART7に続く


PART 1 なぜBASICを高速化するのか
PART 2 効率的な高速化を行うためには
PART 3 BASICの高速化とは
PART 4 どうやったらBASICが高速化できるのか
PART 5 アルゴリズムの見直し
PART 6 メインルーチンの軽量化
PART 7 表示を高速化
PART 8 IF文をうまく使う
PART 9 「BASICだから遅い」は言い訳の言葉

RETURN

inserted by FC2 system