読者です 読者をやめる 読者になる 読者になる

動的型言語のネック

 ゲームオブジェクトと指定したシーングラフノードの間の距離を測るスクリプト関数があったとします。

function(obj, targetNode)
{
    pos1 = obj.getPosition();
    pos2 = targetNode.getWorldPosition();
    
    pos1 -= pos2;
    return pos1.Length();
}

 この書き方を保証するには、それぞれバインドした型が getPosition() や getWorldPosition() などの関数で新しいユーザー定義変数を alloc して返す必要があります。当然けっこう重いし、こんなの毎インター呼ばれたらガベージが一気に溜まってしまう。 alloc を回避する方法として、取得系の関数を参照パラメータにする手がありますが、スクリプタには嫌われる書き方を強いることになります。*1

function(obj, targetNode)
{
    pos1 = Vector3();
    pos2 = Vector3();    

    obj.getPosition(pos1);
    targetNode.getWorldPosition(pos2);
    
    pos1 -= pos2;
    return pos1.Length();
}

 しかし、上のような例だと、どっちみちここまでが限界です。例が悪かった。
 バイトコンパイル段階では2つのローカル変数が使われることが分かるので、マルチ型のローカル変数2つぶんは仮想マシンのスタックフレームから確保されるが、96バイトサイズのユーザー型変数が2つ alloc されることは実行段階でなければ分かりません。 static がある言語なら static に割り当てて2回目以降の割り当てコストを下げるなど、回避策は色々ありますが、スクリプタにお願いできるレベルは越えてるかなと思います。結局、こういう関数は C/C++ 側で実装するのが現実解に……

function(obj, targetNode)
{
    Vector3 pos1, pos2;

    obj.getPosition(pos1);
    targetNode.getWorldPosition(pos2);
    
    pos1 -= pos2;
    return pos1.Length();
}

 仮に静的型言語で、こう書くことができれば、ローカルな96バイトはスタックフレームから供出するようにバイトコンパイルをかけることができますので alloc のコストがかかりません。ということで静的型言語は実行前の検査性だけでなくメモリ周りも強いなぁと思いました。といっても、 alloc の負荷がネックになるのも、回避テクニックが必要なのも今のうちだけだとは思いますが……*2

*1:まぁスクリプタの人は、かくかくしかじかでこう書いてくれと頼めば書いてくれますが……

*2:5年前も同じこと言ってたけど