Optimising (日本語)

多くの場合において Lua における最適化は反直感的です。それがそうであると理論的に"熟知している"としても、変更により性能が改善したと決めつけないでください。常に確認をしてください。常にです。

一般的な最適化の小技

諸悪の根源

Donald Knuth (ドナルド・クヌース) 曰く、
"時期尚早の最適化は諸悪の根源である"

言い換えると、問題による速度低下を確認するまで大量の最適化を行わないでください。

それが全てです。

絨毯爆撃機ではなく、狙撃兵

対象の狙撃距離は、吋(インチ)であり平方哩(へいほうマイル)ではダメだ。

それは CPU の周期とよく似ていて節約・短縮を心がけるものであるため、時間は貴重です。無駄なコードの最適化により速度低下をしないようにしてください。それらはあなたのコードのプロファイルを行い、ボトルネックを見抜いてから、最適化してください。

一般的な Lua の最適化方法

簡潔で快(こころよ)いループの維持

While ループはいかなるプログラムの構築においても有用かつ重要であり、さらにそれらの反復性、および任意の構文内の影響を拡張します。あらゆる性能の小技としてループへ倍数を適用します。

ループの設計時、それぞれの構文ごとにループ本体が本当に必要なのかを考慮を行い、できるだけループ本体の外側へ合法的に移動することでループ完了までに掛かる時間を短縮することができます。

数値 for および ipairs の優先

Lua の table は強力で有用かつ、 Lua への賞賛すべき点であり、非常に高速です。 1 から nまでの全体の数値キーによる要素の端から端まで単にループを行う pairs ではなく、 pairs イテレーターにすることで table にある全要素の端から端まで容易にループにすることが可能になります。それは、しかしながら、テーブルの端から端までループするには最速の方法ではなく、 ipairs よりは高速ですが、小さい for の数値では最速です。例えば "for i=1,n do ... end" です。もちろん、それは上品ではありませんが、それでもなお最速です。

local の使用

Lua において、一般に変数は global (大域、グローバル)、または local (局所、ローカル) のどちらかです。Lua はグローバル環境を確認する前に local 変数の短いリストを保存します。一般に local 変数の探索は global 変数より"本当に明白な速さ"であることを意味します。 通常、この性能の利得は些細なことですが、それが顕著になりうる共通の状況があります。

以下をよく考えてください:

 -- 誤った方法
   for i=1,1000000 do
     localx = math.sin(i)
   end

 -- 正しい方法
   local sin = math.sin
   for i=1, 1000000 do
     localx = sin(i)
   end
  • 誤った方法の用例では、 math.sin() を使用するごとに math を捜して、さらに再度 sin に対して math を捜すため、一度に _G にて二回の探索を要求します。これによりループごとの繰り返しが発生します (約 2,000,000 回の探索)。
  • 正しい方法の用例では、 math.sin() 関数を内包するローカル変数を作成しています。この値はループにて使用されるものです。この部分のコードにより要求された探索回数は基本的に半分になることは直ちに明白であり、さらにローカル変数の探索はグローバル変数の探索より安価であると考えらずにいられません。


LÖVE 特有の小技

群衆の選別

グラフィックス・ハードウェアの基本的なクリッピング能力へ代わりに依存することで、それでゲーム要素の可視性を少しまたは全く考慮しようとせずとも、描画関数の設計を魅力的なものにすることができます。それ以外何もなければ、これは大抵において関数を教えるのは簡単かつ容易にすることができます。

しかし、これらの要素が不可視であっても、それらに対して未だ変形および処理、およびデータをグラフィックス・カードのハードウェアへ送信を行う必要があります。ゲームの設計によっては、外観の計算、または色の混合といった要素に対する追加計算を行う必要があるかもしれません。それらは全て不必要であり、要素として与えるものは実際には不可視、および重要ではないプロジェクトでは、これは不適切な利用による性能の費用負担になります。

オブジェクトに関するハードウェアへいかなるデータの送信を回避することで不可視を決定することができ、さらに要素の可視化が必要なもののみに対する計算を回避するためにコードのリファクタリング(再検証) をしてください。

過剰描画の回避

過剰描画とは画面の一部の上に描画する場合に現在のフレームで既に描画されたものがあるということです。上述の群衆の選別の小技と同じものであるため、これは無駄な努力です。なぜ不可視のものを描画して処理時間を浪費するのですか? 初期時代のグラフィカルなゲームの古い原則は"各ピクセルは一回のみ描き込む"ことです(つまり、過剰描画は発生しない)。その時代から幸運にもハードウェアはずっと進歩しており、なにか別の優位性を提供できるのであれば(簡潔なコードといった)、なにか杜撰でも全く妥当ですが、大量の過剰描画を作成するようなエンジンの設計は回避すべきです。

隠蔽されたループの監視

LÖVE では LÖVE ゲームの生存期間中は love.update(dt) および love.draw() 関数を頻繁に呼び出します。実際のところ、それらは love.run() により隠蔽されたループ本体です。ループへ適用される全ての性能の対価は、これら二種類のコールバックに対しても同様に適用します。

その他の小技


そのほかの言語