FANDOM


Scripters_cafeログ


08/11/22 編集

Monoコンパイルしたスクリプトで変数をリストに格納する場合、スクリプト内で静的に記述してある値は、同じ実体への参照としてリストに追加されるようです。

ただし、これはリストへの追加時と読み込みの時にこのようになっているだけで、特定の要素の値を書き換えた場合、書き換えられた部分は新しい実体になります。

【参考】Babbage Linden Office Hour 2008/09/17

Babbage Linden: the way collection operations work
Babbage Linden: is that they copy references around where they can
Babbage Linden: so, when you add an int or vector to a list
Babbage Linden: it gets boxed in to an object
Babbage Linden: and then a reference to the object is stored in the list
Babbage Linden: as you can't change the value while it's in a list
Babbage Linden: if you copy the list around
Babbage Linden: it just copies the references
Babbage Linden: instead of copying the values
Babbage Linden: so, if you put a string in a list
Babbage Linden: then repeatedly copy the list or append it to another list
Babbage Linden: the runtime copies a 4 byte reference to the string
Babbage Linden: instead of the (many byte) string itself
Babbage Linden: (it's kind of copy on write, yes)
Babbage Linden: copy on unbox

(簡単訳)コレクション操作は、可能な限り参照のコピーを行うように動作
する。リストにintやvectorを追加すると、objectにボックス化された後、
そのobjectへの参照がリストに保存される。なので、リスト中にある値を
直接変更することはできない。リスト全体をコピーする場合、リスト中の
値をすべてコピーするのではなく、それぞれの参照のみをコピーする。
文字列をリストに追加する場合、リストの本体がまたコピーされるか、別の
リストに文字列が追加される。(訳注:この部分正確に解釈できていません
が、要素を追加する場合は必ず新しくリスト全体が生成されるという意味だ
と思われます。)ランタイムは、文字列本体をコピーする代わりに、文字列
への参照である4バイトだけをコピーする。(書き込み時コピーみたいなもの)
ボックス化解除時コピー。

  • ボックス化/ボックス化解除とはCLIで使われる用語です。
  • →wikipedia ボックス化

この挙動はLSL2とは大きく異なります。LSL2では、リストの要素のすべてが実体を持っており、またコピー時も実体がすべてコピーされていました。

検証スクリプト編集

以下のスクリプトは、リストに動的要素を追加し続けるものです。 1400件程度追加されたところで、Stack-Heap collisionエラーが発生します。

default
{
    state_entry()
    {
        string data = "テスト";
        list li;
        integer cnt;
        @loop;
        {
            li += llStringTrim( data, STRING_TRIM );
            ++cnt;
            llSetText((string)cnt + "/" + (string)llGetFreeMemory(),
                <1.0, 1.0, 1.0>, 1.0);
        } jump loop;
    }
}

これを、以下のように静的な要素を追加し続けるようにすると、7600件程度まで追加できてしまいます。

default
{
    state_entry()
    {
        string data = "テスト";
        list li;
        integer cnt;
        @loop;
        {
            li += data;
            ++cnt;
            llSetText((string)cnt + "/" + (string)llGetFreeMemory(),
                <1.0, 1.0, 1.0>, 1.0);
        } jump loop;
    }
}

前者では、スクリプト内で(llStringTrimの実行によって)生成された動的な文字列(ループの度に新しい要素が生成される)への参照がリストに格納されます。リストに格納されているデータとは別に、動的に生成された文字列データもすべてメモリに格納されます。

これと比較して後者は、格納する文字列の本体は1つだけで、リストの要素(文字列への参照)のみが増えていきますので、前者よりもたくさんの要素を格納できています。


全く同じ値をリストに大量に追加する場面は実際にはまれなので、この仕組みを理解してメモリ効率を挙げるような一般的なテクニックというのは難しいですが、リストにNULL_KEYや空文字列を何度も投入する可能性があるなら、グローバル変数で定義したものをリストに追加するように書くと少しは効果があるかもしれません。

★そのために判定処理コードが増えてしまったりしては本末転倒ですが。



このページのTinyURL:http://tinyurl.com/SC-List-element-reference

広告ブロッカーが検出されました。


広告収入で運営されている無料サイトWikiaでは、このたび広告ブロッカーをご利用の方向けの変更が加わりました。

広告ブロッカーが改変されている場合、Wikiaにアクセスしていただくことができなくなっています。カスタム広告ブロッカーを解除してご利用ください。

FANDOMでも見てみる

おまかせWiki