How_to_optimize_for_Mono

ページ名:How_to_optimize_for_Mono

この文書は Jam Meili氏による英文資料を同氏の許可のもと翻訳・掲載したものです。オリジナルの英文との差異を生じないようにするため、文書中に疑義・修正がある場合は直接の編集を避け、注記マークをつけた上でノートまたはMono Howtoへ誘導するようにお願いします --- Nock Forager 2008年9月16日 (火) 16:03 (UTC)

目次

Monoへの最適化手法[]

この技術の基礎となっている背景を説明するにあたり、出来る限りシンプルに理解しやすいように書こうと注意しました。上級もしくは中級のスクリプターの方にとって、また(Monoを)より理解しよう・知識を深めようとしているかたにとって有益な情報となることを望みます。

ひとことで言うと、この文書に書いてあるような手法はシムのラグを軽減するヒントになります。特に多くの人が同じようなものを使用しているような場面では効果が大きくなります。例えば武器などの作者がある程度限られているようなものや、ロールプレイ用のHUDなどがそれにあたります。武器については多くの弾丸をrezするような場面ではさらに効果が大きくなります。

この文書をよりよくするための提案や、文書の書き方そのもの(英語は私のメインの言語ではないので!)について何かあれば Jam Meiliまで気軽にIMしてください。

Monoそれ自体は必ずしも速いわけではありません。が、内部要素を扱う方法がより効率的になっています。そうは言っても非効率なコードは書けてしまいます。Monoによる性能向上を最大に引き出すためにはMonoがどのように動作しているのか、その背景を知ることが大切です。

C++、C#(Monoの基礎となっている言語です)、アセンブリ言語など10以上の言語を10年以上にわたってリアル世界でのプログラマーとしてやってきた経験、コードのデバックやメモリ管理・コンパイラの動作、OS上でコードやメモリがどのように扱われるかの知識を元に SLのMonoエンジンについてどうすれば最適化ができるか記載したいと思います。

MonoベースのLSLコンパイラは今回提供されなくなりましたが、リアルのアプリケーションで使用されているC#/Monoでのコードやデータの取り扱いから考えると、LSLコンパイラもそれらと大差ないものと類推することができます。Monoの開発責任者のBabbage LindenがMonoの構造や背景についていくつか情報提供や確認をしてくれたことはこの文書を書くにあたって大変参考になりました。

ソフトウェアの話をする際、最も重要な部分はメモリ管理です。なぜかというと、ホトンドのバグ、クラッシュ、セキュリティホールはメモリ管理に関係しているからです。良いプログラムはマシンのメモリを大量に使ったりせず、必要最小限で稼動するものです。ハードディスクへのスワップが発生したり、メモリを空けるための処理がかかったりすると動作の何もかもが遅くなります。つまりSLでいうところの「ラグ」にもメモリは関係しているわけです。Monoでは従来と比較して4倍のメモリ空間を使用できることから、このメモリ管理に関する問題は以前よりもより重要になったと言えるでしょう。


さて、LSLにおいて私たちはメモリを空けたり管理したりする必要はありません。端的に言えば私たちにはそれらのプロセスを管理する方法がホトンドありません。LSLでは変数を定義できるのみで、それを開放するすべはありません。C++などでは気を使わなくてはいけないこれらのことも、LSLではエンジン側がすべてやってくれます。このことはよりエラーが発生しにくい手法といえますが、同時にメモリに対するコントロールができないことで、より多くのメモリを消費してしまう傾向にも繋がっています。そういった制限の中でも、Monoエンジンがどのようにメモリを取り扱っており消費しているかを理解すれば、その使用状況に大きな影響を与えることができます。(従来のLSLエンジン - 以降ではLSOと表記します - にはそういった管理機構は不足していました)。次項ではLSOとMonoエンジンとでどのような差が生じているのか、どうすればより良い状態で稼動するスクリプトが作れるのかについて説明します。


LSOとMonoの最大の違い[]

まず、LSOとMonoのもっとも大きな違いはスクリプトから使用できるメモリ量です。LSOでは16Kバイトであった制限がMonoでは64Kバイトまで拡張されています。LSOでは個々のスクリプトに対してそれぞれ16Kバイトのメモリが割り当てられていました。これはスクリプトのコードやデータ量にかかわらず一律でした。ひとつひとつではたいしたことがなくても、それらが1000個、1万個あればシム全体としては大きな差異になります。Monoではスクリプトが必要とするメモリ量が動的に割り当てられますので、LSOでの場合に比較してMonoでコンパイルされたスクリプトはおそらくより少ないメモリで稼動することになるでしょう。また、LSOではメモリ割当は静的な処理でしたが、Monoでは稼働状況に応じて動的に割当が行われるようになっています。以上の事柄から以下のようなTipsが導かれます:


1.スクリプトは分割するのではなく、ひとつのスクリプトにより多くのコードを実装するべきです。大きなサイズのソースコードであっても従来よりもメモリ消費は少なくなるはずです。ただし、機能的な都合によって小さなスクリプトを分けなくてはいけない場合であってもメリットは効いてきます。
  • より少ないメモリで動作します。いくつかの内部機構が(それぞれのスクリプト用に存在せず)ひとつだけで済むからです。
  • ロードおよび初期化のコード、メモリやスレッド管理が少なくてすみます。ライブラリコールやアセット・データベース操作が減り事でCPUタイムやおそらくネットワーク・タイムの消費も減少します。このあたりが特に効いてくる場合(弾丸など)はrezするのも速くなる効果があるでしょう。
  • オールインワンの構成にして複数のスクリプト間で通信を行わないことで、100倍の速度向上も場合によっては望めるでしょう。
  • スクリプト間でデータを共有する処理が必要なくなります。もしいくつかのスクリプトがそれぞれに値を共有する処理をしていたならば、これがなくなる分で全体としての消費量が減るはずです。


2.必要のない変数を減らしたり複数の目的で変数を再利用しましょう。多くの変数を定義するとそれだけメモリも割り当てられてしまいます。LSOではあまり関係がなかったことですが、Monoではこれらのことにも意味がでてきます。


LSOから向上したポイント[]

LSOとMonoで大きく向上したことのひとつに、メモリへスクリプトがロードされるときのしくみがあります。LSOではコンパイルされたバイトコードはインスタンスごとにそれぞれに対して個別にロードされるしくみでした。Monoでコンパイルされたスクリプトはいわゆる .NETアセンブリ(原理的にはライブラリや DLLのようなものです)となりOSのより現代的なローディング処理を使用できるようになります。現代的なソフトウェアアプリケーションではコードとデータセグメントは分けられています。ライブラリがロードされた場合、それは他の多数のアプリケーションからもロードされる場合があるということです。コード自体はメモリ内で変化することがないからできることです。複数のアプリケーションがそのコードを要求したとしてもメモリ内にコードはひとつだけで済むのです。データセグメントだけがそれぞれのアプリケーションごとに割り当てられます。このことはいわゆるコンピュータアプリケーションがメモリ上のコードを共有する仕組みに似ています。インワールドのオブジェクトもコードベースを共有することができるわけです。簡単に言うとアセンブリ(スクリプトのバイナリコードですね)は1度だけロードされればよく、それを使用している複数のオブジェクトで共有して使用されることになります。唯一、インスタンスごとに分けて用意される必要があるのは変数だけです。この原理をうまく利用することができれば同じシム内に同一スクリプトコードからコピーされたスクリプトが数千合った場合などには、MonoとLSOでメモリ消費量に大きな違いがでてきます。


以上の事柄から以下のようなTipsが導かれます。どうすることが寄り効率的か:3.アセンブリにはソースコードは含まれておらずコンパイルされた結果のみ、実行用のマシンコードしかないことに注意してください。システムはあなたの書いたソースコード単位ではなくアセンブリ単位で稼動することになるため、場合によっては まったく同一のソースコードを元にしているのに必要以上に重複してメモリにロードされてしまうということが起こりえます。これを避けるために知っておいて欲しいこと、それはコンパイル処理をするたびに内部的には別の新しいアセンブリが作成されるということです。もしスクリプトをコピーして、同じものをそれぞれをコンパイルしたら、エンジンにとっては技術的にそれらは別のものとして扱われるわけです。同じコードを複数のオブジェクトで使用したい場合、それぞれに新しいスクリプトを作ってソースコードをコピーしてコンパイルするということはしてはいけません。そうではなくスクリプトをコンパイルして、それをインベントリにコピーし、それが必要なオブジェクトにはインベントリにコピーしたものをコピーしていれるようにする必要があります。コンパイルしなおしてはいけません。


4.スクリプトが1度しかロードされないようにできれば、シムサーバーは余計なメモリを使用しなくてすみます。ひいてはリソースの余裕も増加しますし、ハードディスクへのスワップをしなくて良くなる分のラグの減少にもつながります。スクリプトが、たったひとつのオブジェクトに対してでも一度でもロードされているならば、それはハードディスクから読み直したり初期化したりする必要はないのです。これは弾丸をrezするようなrezzerなどの場合に特に効果がでてくるでしょう。


5.もしどうしても必要な場合は、シム内に存在するコードはrezされているオブジェクトのインベントリ直下に存在するようにしてください(オブジェクトの中にあるオブジェクト内のスクリプト、ということがないように)。最初の一回目のrezは初期化処理の分時間を食ってしまいますが、このようにスクリプトを配置することで、同じスクリプトが繰り返しrezされる物のような場合、初期化処理を少し省略することが出来る分、初期化自体がすこし早くなる効果があります。


6.同一シム内で広く使われるようなコード(例えばコンバットシムでの銃弾のような)を共通化することでスクリプトの稼動状況を向上することが出来ます。スクリプトを作る際に、例えば他の武器と同じ機能やサブパートになっている部分を統合したりして より汎用化・統合化をすることもできるでしょう。同一コードを使うけれどもオブジェクトの名前やインベントリ内のオブジェクトを判定して異なる武器として必要な動作だけをするようにするということもできるでしょう。製品で言えばライトバージョンとプロバージョンなどのタイプ違いなどにもこういったことが適用できます。注意:ソースコードが同一だったとしても、再コンパイルをしてしまうと、それは別のアセンブリになってしまい、前のものとは共有化されなくなるということに注意してください。ソースコードに修正をしなくてはいけない場合以外は最初にコンパイルしたバージョンをコピーしてコードを共有化するようにしてください。共有化を勧めることはとても重要なポイントです。単にメモリに影響するだけではなく様々な点でよい作用がでてきます。例えばCPUキャッシュのレベルでも、LSOがまったくキャッシュが効かなかったのに比較して向上することが期待できます。可能な限り共有化をすることでスクリプトが消費するcpu time全体も減る可能性もあるということです。


7.全てのスクリプトを再コンパイルしなくてはいけない場合を除いて、"Tools -> Recompile scripts in Selection"は使用しないようにしましょう。全部をコンパイルし直してしまうことで、せっかく共有化できていたスクリプトが製品のバージョン間で切り離してしまうことになります。自分が流通させているスクリプトのバージョンに気を使って、コードが変更にならない限り以前から流通させているものと同一の再コンパイルさせていないものを使うように注意しましょう。スクリプトの名称や詳細説明の欄を使ってリビジョン番号などを管理して、どれがどれだかわかるようにしていきましょう。


ひとつ明確にしておくポイントがあります:異なるシム間ではコードの共有化は行われません。これはシムがそれぞれ別のシミュレータ上で稼動しているからです。この場合、相互にはまったく影響は生じません。


Monoのメモリ管理について[]

Monoではメモリが動的に割り当てられるようになりました。次にこの点から検討を行ってみましょう。リアルでのアプリケーションでは速度向上のための手法として、小さなアプリケーションの場合はライブラリを多用してその都度呼び出しを行うということは避けて、かわりに事前にメモリへ読み込んでしまうという手があります。LSLではメモリの割当や開放を実際にはコントロールすることはできないですが、これはどのように適用できるでしょうか。


8.一時的な変数をたくさん設定することを避けましょう。例えばイベントや関数ごとに変数を定義して使うといったようなことです。さらにその関数が頻繁に呼び出されるようなものだとさらに悪い影響がでてしまいます。コードを見直してあまりおかしくならない範囲で、繰り返しコールされるような状態を避けたり、グローバル変数を使用したり、変数の再利用をしたりすると良いでしょう。可能ならループに入る前に変数を初期化するようにしましょう。これでどれくらい差がでるのか?と疑問に思うことでしょう。添付のサンプル(3つのベンチマークテストを入れてあります)を使って自分自身で確認してみてください。再コンパイルしてみる以外に、単純にスクリプトをリセットするのも試してみると、初回と二回目でキャッシュが効いて動作が100倍ほども高速になることがわかるでしょう。また、最後の手法が必ず一番早く動作することも確認できると思います。(テストはほとんどなにもないシムで行いました。高負荷下の環境では、必ずしも同じ結果にならない可能性があります)。Mono Benchmark variable usage オブジェクトに入れて、再コンパイルして、タッチして実行です。LSOでも動作させてみてください。1番目のテストはMonoの3倍程度、2番目のテストは10倍は長くかかります。また、LSOではMonoのようにキャッシュが効かないため二回目の実行でもホトンド速度向上は見られません。Monoでキャッシュが効いている場合と比較すると240倍というようなものすごい速度差があらわれてきます(TIP:サンプルを流用して自分独自のベンチマークを作ってみましょう!なおテストをする際は必ず複数回、それもある程度十分な回数テストを実施するようにしてください。その際には単にスクリプトをリセットするのではなくコンパイルしなおすようにしましょう。そうすることで適切で評価に値する結果が得られるようになります。)


9.単純だが効果的な方法:オブジェクトに含まれている必要のないスクリプトをすべて消してください。例えばフローティングテキストを表示したり(llSetText)、パーティクルをセットしたり(llParticleSystem)、テクスチャをセットしたり、色やプリムの各種パラメータを定義したり、オブジェクトを回転させたり(llTargetOmega)、テクスチャをアニメーションさせたり(llSetTextureAnim)。こういったもので、1度しか使用しないようなものも働き続けているということを意識して、必要がないスクリプトは削除するように意識するべきです。何もしていない idle状態のスクリプトでも cpu-timeとメモリを消費します。MonoではLSOよりもこの影響が大きくなっています。もし将来の変更のためなどの理由でスクリプトを残しておきたいのであれば、"Running"ではなく停止させておくことも検討してください。実際、動作させておく必要はないはずです。


誤った解釈に注意:[]

使用できるメモリが増えたからといって、従来の四倍のリストを使えるとは考えてはいけません。これは必ずしも当てはまりません。これを理解するためにはMonoがLSOとは異なって、どのようにデータタイプを取り扱っているかについて知る必要があります。LSOでは内部的に文字列はUTF-8のコードとして格納されていました。これはつまり目に見えるキャラクタは 1から3バイトのメモリを占めるということです。MonoではそれらはUTF-16で格納されます。つまりキャラクタごとに2バイト消費するということです。単純なアルファベット、数字、特殊キャラクタの付かない句読点、などはLSOと比較してMonoでは倍のメモリを消費することになります。したがって最悪の場合、LSOと比較して使用できるメモリ量は2倍までということになってしまいます。内部的な格納方法はリストの場合でも異なっており、MonoではLSOと比較してエントリごとに少し多くのメモリを消費します。したがって単純に使用できるメモリが4倍になった!と考えてしまうと、実際に使用できるリストの長さは あなたが思っているよりも少ないという結果になる可能性があります。リンデンラボが今回メモリ上限をLSOスクリプトの4倍に増やした理由もここにあります。上限を上げることによって大きなリストを必要としいる既存のコンテンツが破綻しないようにしたのです。また、floatやkey、stringではなくintegerを使うようにしましょう。テストでもstringはkeyよりも効率的という結果が出ています。この文章を記述している時点でグローバル変数が倍のメモリを消費してしまうバグが存在していますので注意してください。この影響で、非常に大きなリストを使用する場合、LSOで大丈夫だったものがMonoでは稼動しないという場合があります。これは近いうちに修正される予定です。(http://jira.secondlife.com/browse/SVC-2715)


また、最適化のテクニックは必ずしも常に有効とは限らないことにも注意してください。多くの場合、これらの効果は対象のコードに依存します。すべての場合についてカバーできるようなテクニックを見出すにはあまりにも条件が多岐に渡りすぎています。コードを駆使してLSLで可能な範囲でプロファイリングを行いベンチマークをすることで初めてどのようなコードがベストなのか知ることができます。その一方で、たった一度しか実行されなかったり、メモリの差や効果がホトンドないようなものに対して多大な労力をかけて調査するのはナンセンスともいえるでしょう。コードの最適化を行うアルゴリズムは実世界のソフトウェアに利用されている方法を参考にするのが役に立ちます。例を挙げると、同じ答えを返す関数を繰り返し繰り返し呼び出すのではなく、答えを変数に格納してループの中ではこれを繰り返し使用する、などのコーディングにおいて一般的に言われているようなテクニックはLSLでも同じように効果があるわけです。もう一点、LSLコードの最適化手法として挙げられている手法、特にlslwiki.netに示されているようなものはLSOエンジンのバイトコード処理に依存しています。実のところLSOのコンパイラは最適化はほとんどしていませんでした。これはMonoにはあまりあてはまりません。有名な例では例えばリストメモリーに関するハックは意味がなくなっています。これはLSOであったようなリスト管理でのメモリのムダがMonoでは発生しないからです。実際のところリストの実装方法は従来とはまったく異なっています。ということで、最適化の指南を参照する場合は、それがMono向けのものかどうか注意するようにしてください。


著者情報[]

Autor: Jam Meili、Date: 2008-09-04、Provided free for Members of the Group "SCRIPTS"

この文書を販売したりフリービーのパッケージに含めたりすることは禁止します。その代わりに上記のグループを参照するように促してください。

(C)opyright 2008 by Jam Meili - All Rights reserved.

Translation into Japanese done by Nock Forager. 翻訳に関する疑問点は Nock Foragerまでお願いします。



特に記載のない限り、コミュニティのコンテンツはCC BY-SAライセンスの下で利用可能です。

シェアボタン: このページをSNSに投稿するのに便利です。


最近更新されたページ

左メニュー

メニュー 用語集 - これはどういう意味?用語解説 Q&A - よくある質問、定番解説、公式サポートに関する情報 トラブル・シューティング - トラブル・シューティング情...

顔文字

すでに日本の文字文化の仲間入りしたグラフィックを駆使しない表現手段。これが英語と日本語で違う。基本的に英語の顔文字は横向き。最初、わからなかったのが、この2つ...

銀行業務に関する判断・事例

2008年1月22日より施行される「銀行」に関する判断事例をまとめます。これらは「参考」であり、これによって「判断」をするものではありません。個々に記載されていない背景がある場合もありますので判例とは...

都市伝説&うわさ

目次1 Rumor & urban legend1.1 セカンドライフ全人口のうち3割はボット、半数は未使用のアカウントらしぃ1.2 キャッシュはまめにクリアしたほうがよい?1.3 支払い情報がないと...

身長

意外と知られていないが、AVの身長を計れる。llGetScaleのvectorのzを使う。ただし、靴などは脱いで計ること。SL内では2m前後が普通なようで まじめに計って 160cm程度にしたとある人...

設定

Second Lifeでは、様々な設定を行うことができます。「編集」→「環境設定」か、Ctrl + Pで呼び出せます。特に記載のない限り、コミュニティのコンテンツはCC BY-SAライセンスの下で利用...

複数アカウントの管理

このページの内容は公式 Knowledge Base「Alt Account FAQ」を翻訳、一部補足したものです。内容は予告なく変更される場合があるため契約を行う際は原文を確認するようにお願いします...

英語

RL/SLを問わず、世界標準語(なのか?)。人口比率的に、普通に生活していると英語を話す機会は多い。英語chatの参考に...ねっとげーむのえいかいわ。ちなみにある人の変遷 (ある人は、高卒程度の英語...

略語

日本語ほど省略しまくる言葉もないと思っていたら英語には勝てませんでした...例えば...lollaugh over laud大笑いbrbbe right backすぐもどるhbhurry back早く...

用語集

すごい作りかけです。みなさん、御協力お願いします。SIMSL内で作成される島のこと。LSLLinden Script Languageのこと。これで、オブジェクトやアバターを操作出来る。アバターSL内...

最近の出来事

最新情報です[]パスワードの盗難事件等発生していますいので、パスワードの再確認をお願いします。 --Keiji 2007年6月17日 (日) 16:46 (UTC)特に記載のない限り、コミュニティのコ...

日本人向けSIM

ここでは、日本人向けSIM(日本人利用者が多い、日本人が運営しているSIM)を並べていきます。特に記載のない限り、コミュニティのコンテンツはCC BY-SAライセンスの下で利用可能です。...

換金

日本円 →(US$)→ L$ / L$ →(US$)→日本円といった流れで換金が可能。リンデンラボ公式LindeXのみ。その他の方法Paypal経由でサードパーティを利用。例:ANSHECHUNG.C...

太陽

太陽を動かすことも可能。SLの太陽(及び月)は動くので、明るさも当然変わる。暗くなると見えにくいため、太陽を出して明るくすることもできる。メニューバーから以下の操作でコントロール。World - Fo...

外部サイトリンク

活動の軸が外部にある関連のリンク。ニュース、技術資料、ツール、サービスなど。イン・ワールドで活動している団体の説明 HPなどはこちらではなくイン・ワールド系リンクへ記載してください。目次1 ニュース1...

基本操作

初心者講座 > 基本操作ここでは、基本操作を解説します。画面の見方[]キーボードによる操作[]カメラ操作[]特に記載のない限り、コミュニティのコンテンツはCC BY-SAライセンスの下で利用可能です。...

土地の種類・分類

目次1 土地の種類2 土地の購入2.1 メインランドで土地を購入する場合2.2 プライベート・アイランドで土地を購入する場合2.3 レンタルとは?3 その他注意事項土地の種類[]セカンドライフにおいて...

土地

SL内では土地を売買・賃貸できる自分の(または所属するグループが)土地をもつと、そこを Homeとして設定できる。(CTRL+SHIFT+Hで、すぐに帰れる場所になる)土地のプリム数の範囲内で、家など...

右クリック

パイ・メニューを開いてその人や物に対して何か行う操作。何かしたい/情報を知りたいという時は、右クリックすべし。macな人に「右クリックないんですけど…」と質問されるのは定番。Optだったかを押しながら...