2025/10/23 14:55 Luau's Performance

やっほー、ロボ子! 今日も元気じゃな?

はい、博士! 今日も新しいことを学ぶのが楽しみです。

今日はLuauの最適化について話すぞ! LuauはLuaをベースにした言語なんじゃ。

Luaは組み込み用途でよく使われるスクリプト言語ですね。Luauはどのような点が最適化されているんですか?

Luauはね、まずインタプリタがC言語で書かれていて、ClangとかMSVCでコンパイルすると、めっちゃ効率的なアセンブリを生成するように調整されてるんじゃ!

C言語で書かれているんですね。Lua 5.xよりも高速で、LuaJITのインタプリタに匹敵する性能を持つ場合もあるとのことですが、具体的にはどのような処理が速いんですか?

それがね、x64環境だとインタプリタのコアループとか組み込み関数が約16KBにコンパイルされるらしいんじゃ。コンパクトで良いね!

最適化コンパイラもあるんですね。ソースコードをASTに解析して、そこからバイトコードを生成するんですか。

そうそう! コンパイル時にちょっとペナルティはあるけど、より柔軟なコードと最適化されたバイトコード生成が可能になるんじゃ。最適化レベル2(`-O2`)を使うと、さらに積極的な最適化ができるぞ!

型情報を使った最適化もできるんですね。静的型付けのメリットを活かしているんですね。

その通り! それから、イプシロンオーバーヘッドデバッガっていうのもあるんじゃ。ブレークポイントとかシングルステップをサポートしてるんだけど、ブレークポイントがあってもスクリプトの実行速度が落ちないのがすごい!

デバッガまで最適化されているとは驚きです。テーブルとグローバルアクセスに対するインラインキャッシュもあるんですね。フィールドルックアップが速くなるんですか?

そう! フィールド名がコンパイル時に分かっていれば、フィールドアクセスがめっちゃ速くなるんじゃ。オブジェクト構造が均一だとさらに良いらしいぞ。

`math.max`のようなグローバルチェーンのインポートも最適化されているんですね。ロード時に解決されるとのことですが、`loadstring`、`getfenv`、`setfenv`を使うと最適化が無効になるんですか。

そうなんじゃ。環境テーブルの形状を予測できないと最適化できないからね。あと、メソッド呼び出しも最適化されてるんじゃ! `obj:Method`構文を使うと、特別な命令シーケンスが生成されるらしいぞ。

オブジェクトがLuaテーブルの場合、VMがインラインキャッシュに基づいてメタテーブルを介してメソッドの実装を迅速に発見するんですね。オブジェクトがリフレクトされたuserdataの場合はどうなるんですか?

userdataの場合は、「namecall」っていう特殊なメカニズムを使って、相互運用コストを最小限に抑えるんじゃ。

組み込み関数呼び出しも最適化されているんですね。`math`、`bit32`、`string`、`table`ライブラリの関数が最適化されているとのことですが、どのような仕組みですか?

純粋な環境では、「fastcall」メカニズムを通じて最適化されるんじゃ。関数呼び出しがコンパイラにとって「明白」である必要があるらしいぞ。

テーブルのイテレーションも最適化されているんですね。`for .. in t`、`for .. in ipairs(t)`、`for .. in pairs(t)`のイディオムを認識して、最適化されたバイトコードを生成するとのことですが、パフォーマンスは同等なんですね。

そうなんじゃ! どのイテレーションを使ってもパフォーマンスが変わらないのは嬉しいね。

テーブルの長さも最適化されているんですね。`#t`の要素がテーブルの配列部分に格納されていることを保証するとのことですが、最悪の場合の複雑さはO(logN)なんですね。

テーブルの長さをキャッシュして、`table.insert`や`table.remove`などの操作後に調整するのもポイントじゃ。

テーブルの作成と変更に関する最適化もあるんですね。オブジェクトのようなテーブルを作成する場合は、テーブルリテラルを使うのが良いんですね。

そう! 配列のようなテーブルを作成する場合は、`table.create`関数を使って、事前に割り当てられたストレージを持つ空のテーブルを作成するのがおすすめじゃ。

ネイティブベクトル数学もサポートしているんですね。3つのコンポーネントを持つ32ビット浮動小数点ベクトルを格納できるネイティブ値型を提供とのことですが、VMはすべての数学演算とコンポーネント操作をサポートするんですね。

ゲーム開発とかで便利そうじゃな!

アップバリューストレージも最適化されているんですね。変更されないアップバリューを優先するように再設計とのことですが、変更可能なアップバリューには、追加の割り当てられたオブジェクトが必要なんですね。

クロージャキャッシングも面白いぞ。同じ関数式の複数の実行が意味的に同一の関数オブジェクトをもたらすことが保証されている場合、クロージャをキャッシュして、常に同じオブジェクトを返すんじゃ。

ガベージコレクションも最適化されているんですね。比例-積分-微分コントローラを使って、ターゲットヒープサイズに到達するための内部ガベージコレクタの調整値を推定するペース配分アルゴリズムを実装しているんですね。

そう! あと、コルーチンのインクリメンタルマーキングも実装されてるんじゃ。Luauの弱いテーブルは、`__mode`文字列の一部として`s`を含めることで「縮小可能」としてマークできるらしいぞ。

最適化されたガベージコレクタースイープも使用しているんですね。ページ化されたスイーパーを使用しているとのことですが、関数のインライン展開とループのアンローリングも行われるんですね。

最適化レベル2を有効にすると、関数のインライン展開とループのアンローリングが実行されるんじゃ。コンパイル時にループ境界が既知のループのみをアンロールできるらしいぞ。

ローカル関数のみをインライン展開できるんですね。Luau、かなり徹底的に最適化されているんですね。

じゃろ? これでロボ子もLuauマスターじゃ!

ありがとうございます、博士! 頑張ります!

そういえばロボ子、Luauで作ったプログラムが速すぎて、時間が余っちゃった! どうしよう?

時間が余ったら、もっと難しい問題を解いてみましょう!

それもそうじゃな! って、オチが弱いぞ!
⚠️この記事は生成AIによるコンテンツを含み、ハルシネーションの可能性があります。