トピック
ユウリ yuuri.com

高速化への道

プログラムを組んでいて、初期に作ったものと改良したものとを比較したとき、だいぶ処理速度が遅くなっているなと気づきました。 処理速度を向上させるようなプログラムの書き方などありましたら教えていただけますでしょうか。
7そうだね
プレイ済み
返信[1]
親投稿
スー thanks_0u0
私が普通のライフゲームを作った時に使ったのとか、とりあえず知ってる高速化の方法ですー( ´ ▽ ` )ノ ・ビットボードにより32個のセルを一括計算 ・GRP4画面に0〜65535のパターンを予め用意して、GPSETではなくGCOPYを使って16dotずつ描画 ・ビットシフトに置き換えられる掛け算や割り算は置き換え ・ループを展開する ・配列アクセス(やたら遅い)をなるべく減らすために、何度も同じ配列を使う場合は一度変数に入れる ・二次元配列より一次元配列のほうがちょっと速い ・ループはREPEATが一番速い 体感で効果が高かったのは、描画を減らすのと配列アクセスを減らすのです。高速化難しいですよね(・u・)
8そうだね
プレイ済み
返信[2]
親投稿
・グラフィックの書き換えは GSAVE→計算→GLOAD FOR-NEXTとGPSETよりはまともになる ・==0はやめて!(論理反転)に 両辺を評価しない分速いとか ・変わらない色はRGB()で表さない これも関数だと通るたび評価するので定数にします
5そうだね
プレイ済み
返信[3]
親投稿
高速化の落とし穴編 ・ビットシフトは型に注意 実数型の値は整数型に変換する手間がかかるので遅くなる場合もある。
5そうだね
プレイ済み
返信[4]
親投稿
定数の型 ・&Hか&Bを使っている ・小数点が無く、32bit符号付きに収まる範囲 →整数型 ・指数表記を使っている ・小数点がある ・32bit符号付きからはみ出る数 →実数型
3そうだね
プレイ済み
返信[5]
親投稿
・整数型の速度を生かすならOPTION DEFINT 単に変数%にしても速くならなかった Q=100 Q%=100 OPTION DEFINTの環境でQ=100 簡単に計った速度比が上から 32:31:30 (※ループ用のFOR-NEXTが含まれていることに注意)
2そうだね
プレイ済み
返信[6]
親投稿
あ、おバカなミスがあったので上の速度比は忘れてください(ループ変数の型固定忘れ)
2そうだね
プレイ済み
返信[7]
親投稿
Q=100 Q%=100 OPTION DEFINTの環境でQ=100 空ループ 31 30 29 23 ※ループ変数は%で整数型強制 値は「ミリ秒 DIV 100」。バラつきは±1ぐらいあるので目安で。
3そうだね
プレイ済み
返信[8]
親投稿
中間コードの効果で、高速化の常識が一部通じないのがプチコンが特徴です… (変数名長を気にするのは無意味とか)
5そうだね
プレイ済み
返信[9]
親投稿
だにえる haru2016nen
DEF~END よりも @ラベル~RETURN の方が呼び出しが速い。 乗算よりも徐算の方が速い。 A=A*2 ⇒ A=A/0.5
6そうだね
プレイ済み
返信[10]
親投稿
MIKI ifconfig
r=asc("×") print " " + chr$(r)+"="; right$("00000"+str$(nr),5) よりも r$="×" print format$(" %s=%05d", r$, nr) の方が速い。 配列アクセスは基本遅いので、回避する努力を。 gosub で飛んだ先で該当判定するより、該当判定してから gosub した方が速い。 同じセルに複数種族が同居することはないのだから、 world[x,y] が現世代の種を現すようにすればいいかも。 これらを踏まえて(続く)
4そうだね
プレイ済み
返信[11]
親投稿
MIKI ifconfig
こんな感じ for x = 0 to xm - 1 for y = 0 to ym - 1 var s = world[x,y] '変数に入れて配列アクセスを減らす if s == 空白 then gosub @space elseif s == 植物 then gosub @plant ... endif next next
3そうだね
プレイ済み
返信[12]
親投稿
MIKI ifconfig
計算と表示を同時に行うより、全セル計算してから表示の方が、現状の locate + print をセル数分繰り返すより速い。 その場合、表示は for y = 0 to ym - 1 locate 0, y for x = 0 to xm - 1 var s = world[x,y] color col[s]: print cg$[s]; next print next などとして locate を省きます。col[] には種ごとの色、cg$[]には種ごとの文字を入れときます。
3そうだね
プレイ済み
返信[13]
親投稿
ユウリ yuuri.com
たくさんのコメントありがとうございます! 私の知らない世界がいっぱい過ぎて理解が追い付けないところもありますがステップアップ目指して頑張ろうと思います。 すーさんのライフゲーム手元に落としてありました。改めてみてとっても速くてびっくりです。
2そうだね
プレイ済み
返信[14]
親投稿
おちゃめ ochame_nako
昔の8bit機を使っていた頃は処理速度は切実な問題だったので私も様々な方法によって高速化を行ってきました。 まず、高速化において最も重要なのはボトルネックとなっている部分を知ることです。表示がボトルネックになっているのか判定処理がボトルネックになっているのかを知らずに行っていると頑張って高速化した割りには効果が薄いという結果になってしまいます。 高速化において一番重要なのはアルゴリズムの改善です。同じ処理を行うのにその計算手順、処理方法は1つとは限らず無数の方法があります。その中で最も高速なものを選ぶと良いです。 高速化においてアルゴリズムの改善と同じくらい重要なのがメインルーチン内の処理を減らすことです。メインルーチン内では不要な処理があったらそれを省くというのが高速化に繋がるというだけではなく多重ループになっている場合はループ回数を減らすことが高速化に繋がります。
7そうだね
プレイ済み
返信[15]
親投稿
おちゃめ ochame_nako
そして、次に他の皆さんが書いているような高速になる記述方法が重要になってきます。 これは様々なものがあり書き切れませんが、プチコン3号特有のものとしては乗算より除算の方が高速であるため乗算を除算に書き換える(X*2はX/0.5にするという感じ)、配列変数は極端に遅いためできるだけ使わないようにする(まとめて処理可能な場面ではARYOPを使うことで最大90倍の高速化が可能)というやり方などがあります。 実際にどの程度の効果があるかは元のプログラム次第ですが、数倍程度の高速化は十分に可能だと思います。 しかし、元のプログラムがすでに速い場合はそれを体感することは難しいです。(60fpsを超えてしまうとほとんど無意味)
5そうだね
プレイ済み
返信[16]
親投稿
おちゃめ ochame_nako
上記の方法によって私は昔は雑誌に掲載されていたゲームを高速化改造のみで平均3倍(最高9倍)の処理速度のアップができました。 ただし、高速化は追求すると可読性が落ちたり、メインテナンス性が落ちたりします。 仕様を変えたいと思った際には対応が難しくなったりするためそこまで限界まで高速化が必要なのかも考えておくと良いでしょう。 私が昔ポケコンで作っていたプログラムは限界まで処理速度を稼ぐため可読性、メインテナンス性は無視してひたすら高速化を行いました。 そのお陰でベーマガに掲載されたゲームはオールBASICのゲームなのに編集部の人に「マシン語で作られたゲーム」と誤解されるくらいの速度に達することができました。 元が遅ければ遅いほど高速化の効果は抜群ということです。
5そうだね
プレイ済み
返信[17]
親投稿
おちゃめ ochame_nako
高速化の実際の例としては私の簡易地球儀QSPを見てください。 QSPで出来る範囲内の高速化処理を行っていて3DSでテクスチャ付きの球体をグリグリ回転させることが可能です。 https://miiverse.nintendo.net/posts/AYMHAAADAAB2V0feIaGUYA とはいえ、今見たら無駄な部分が見つかったのでもう数%の高速化は可能だと思います。(QSPに拘らなければ桁違いの高速化も可能)
5そうだね
プレイ済み
返信[18]
親投稿
れい rei-nntnd
一番深いループだけ気にしときゃいいよ
8そうだね
プレイ済み
返信[19]
親投稿
重たいのって ・命令が重たい(絵を描く、配列アクセスなど) ・やりかたが重たい(アルゴリズム) に分けられて、この組み合わせで処理が早くなるものを選ぶという感じです アルゴリズムとして、ループがFOR X: FOR Y: FOR C などで3段階になっていれば、最後のFOR C の処理は X*Y*C回数分実行されて、ここに重たい処理(命令・アルゴリズムGOSUBやDEF)があると全体として重たくなります。 その処理が1つ前のループで処理できないかとか、ループの中で処理しなくてもいいかとか、そもそも別のやりかたがあるのかとか、処理を考え直すのも高速化につながります 命令としては表示のように値1つ渡して1回表示する命令を何回も呼ぶより、まとめて値を渡して一気に表示する命令のほうが早いです (内部処理として表示準備処理を何回も実行するか、まとめて1回するかの差)
8そうだね
プレイ済み
返信[20]
親投稿
計算処理がある場合、変数が影響しないのであれば使用する範囲ですべて計算しておいて、その計算済み結果(定数)で置き換える、のもあります。 MIKIさんの col[] に色を先に計算しておく小さなことから、 スーさんのGRP4に0~65535パターン全部書いておく大きなところまでいろいろとあります。 変数が影響する計算でも、変数で計算する前までの値を先に計算することで早くなる場合もあります。 たとえば、三角関数系の計算が重たいので256(の倍数)くらいの配列を0度~90度として先に計算しておくのもよくあります。(マイコン系の世界だとまだまだ現役) ※おちゃめさんのQSP(QuarterScreenProgramかな? 1/4画面でおさまるプログラム)にこだわらなければ高速可能なのはたぶんコレ 結局重たいのはいろんな重たい処理×実行回数(ループ回数)なので、ちょっとずつ減らしてく感じです
5そうだね
プレイ済み
返信[21]
親投稿
おちゃめ ochame_nako
たんじぇさんへ 実はプチコン3号の場合、昔のBASICとは異なり三角関数が異常に速くて配列が異常に遅いためあらかじめ配列SNにSIN値を入れてA=SN[X]とするよりもA=SIN(X)と普通に三角関数を計算する方が速いのです。 ちなみに簡易地球儀は回転が横方向だけであるためあらかじめ回転済みのGRPを配列に入れておけば1ドット単位で演算(約13000回のループ)がGLOAD一発で済むため桁違いの高速化が可能になるということです。(スーさんが書いているやり方に近いです) ただし、あらかじめ演算処理が必要であるため起動に時間がかかるとかメモリの問題で回転処理の分解能が下がる(BIGならば問題ない)という問題もあります。 高速化といっても手法はピンからキリまでありどのような手法が許容できるかによっても変わります。
6そうだね
プレイ済み
返信[22]
親投稿
れい rei-nntnd
プチコンは変態ハードなので 三角関数は配列で持つより計算したほうが速い a*b/cはa/(c/b)とした方が速い ループはREPEATが一番速い 定数より変数の方が速い incよりa=a+1が速いし、さらにb=1としておいてa=a+bが一番早い。 aが実数型でもbは整数型が速い なのでループでは I=0:P%=1:REPEAT 処理 I=I+P%:UNTIL I>=N が最速
9そうだね
プレイ済み
返信[23]
親投稿
ユウリ yuuri.com
MIKIさん >GOSUBで飛んだ先で該当判定するより、 >該当判定してからGOSUBした方が速い。 GOSUB @LABEL @LABEL IF 条件式 THEN ・・・ ENDIF RETURN としていたのですが、 IF 条件式 THEN GOSUB @LABEL と変更したところ、劇的に早くなりました。
1そうだね
プレイ済み
返信[24]
親投稿
ユウリ yuuri.com
>同じセルに複数種族が同居することはないのだから、 植物や死骸の上を動物さんが走ったり、寝転んだりするのでちょっと困っています。 また、動物同士では同居することはありませんが、植物や死骸は同士で同居することがあります。 植物の上で動物が死ぬと肥料となって、ひとつのセルに植物セルが2以上になるようなルールです。 死骸も同様の処理をしていたと思います。 動くモノと動かないモノで処理方法を変えて組み合わせたほうがよいのかな。
0そうだね
プレイ済み
返信[25]
親投稿
ユウリ yuuri.com
>計算と表示を同時に行うより、全セル計算してから表示の方が、 >現状の locate + print をセル数分繰り返すより速い。 ここが私にとって一番の難関です。 計算と表示の分離が今のところうまくできていません。
0そうだね
プレイ済み
返信[26]
親投稿
ユウリ yuuri.com
おちゃめさん 簡易地球儀QSP見てみました。 1行にまとまったプログラムで、地球を回せるなんてすごいですね! 美しさみたいなのを感じます。
0そうだね
プレイ済み
返信[27]
親投稿
MIKI ifconfig
同じ種が重ならないのであれば、ビットごとに種を割り当てる方法があるのだけれど、同じ種が重なるのかあ・・・ であれば現状の、種ごとに配列持つ実装方法がいいでしょうね。 >植物や死骸の上を動物さんが走ったり、寝転んだりするのでちょっと困っています。 ソースよく読んでないので見当違いだったらごめんね。 こういうシミュレーションでは「現世代」の配列と「次世代」の配列を分けるのが常套手段です。 現世代の配置を元に計算するのですが、死亡や移動などの結果は次世代配列にセットします。 全セルの計算が終わったら、次世代配列を現世代配列に copy します。 (配列の代入の方が速いのですが、「参照」の概念が出てくるので、まずは copy オススメ)
1そうだね
プレイ済み
返信[28]
親投稿
MIKI ifconfig
> 全セル計算してから表示 先に書いたように、現世代と次世代を分離すれば、 全セル計算終わった時点で次世代配列は完成しているので、 それを一気に表示すればいいのです。 スーさんのライフゲームもたぶん現世代と次世代を分けていると思います(ソース見てませんすみません)
2そうだね
プレイ済み
返信[29]
親投稿
しんいち stgf1080
素晴らしいトピ立ってるじゃないですか~。貴重な情報満載でありがたすぎです(^o^) おかげさまでビー玉ゲーム無印3DSでも60fpsに収めることができたみたい。 ライフゲームと関係なくてごめんねm__m #嬉しすぎてつい(^^ゞ
6そうだね
プレイ済み
返信[30]
親投稿
>実行速度が配列より三角関数の方が早い BIG出たころにライフゲームっぽく854x480x2の配列でデータ計算させて全ピクセルにGPSETしたら全然フレームレートでなかったから、GPSETが重たくてBIGだと配列もある程度重たいんだろうなぁとは思ってはいたけど、 そこまでプチコンの配列って重たいんですね。 結局配列が重たいから回避したい場合って ・配列使わないで計算で対応できれば計算で回避 ・どうしても配列を使わないといけないところの高速化はプチコンじゃ難しい という感じなのかな? 表示しないGRPnを512x512配列で使うとか、使わないスプライト管理領域(SPDEF,SPVAR)やBG管理領域(BGPUT/BGGET)を構造体や配列に使うとかを実験してみたら早くなりそうなのかも?
3そうだね
プレイ済み
返信[31]
親投稿
ユウリ yuuri.com
ストップウォッチ片手に高速化処理に向けて毎日3DSとにらめっこする日々を過ごしています。 今のところ、元より2.3倍ほどの高速化に成功しました。 修正を行った点は以下5項目です。 ・OPTION DEFINTを使用 ・FOR文中のGOSUBを減らす ・該当判定してからGOSUB ・ループを展開する ・ループはREPEATを使用する
1そうだね
プレイ済み
返信[32]
親投稿
ユウリ yuuri.com
メモ:FOR文をREPEATに置き換える ============== FOR Y=0 TO MY FOR X=0 TO MX 処理 NEXT NEXT ============== Y=0:P%=1:REPEAT X=0:REPEAT 処理 X=X+P%:UNTIL X>MX Y=Y+P%:UNTIL Y>MY ==============
0そうだね
プレイ済み
返信[33]
親投稿
ユウリ yuuri.com
・IF文の比較演算子を可能ならば省略 IF X==0 THEN→IF !X THEN IF X==1 THEN→IF X THEN
0そうだね
プレイ済み
返信[34]
親投稿
ユウリ yuuri.com
全て計算してから一気に描画する作戦は失敗に終わりました。メイン内のループ数が増えてしまうことによって逆に遅くなってしまったのかも。 配列アクセス回数を減らす作戦も失敗しました。変数に代入する分遅くなってしまいました。元々そんなにアクセス回数多くないこともあるかも。 何はともあれとりあえず十分に高速化できたので、これで閉めたいと思います。 コメントくださった皆様ありがとうございました!
2そうだね
プレイ済み