NodeGraph等のInstantiateの負荷について

ここは、Arbor開発者への要望を行うためのフォーラムです。
要望に必要な項目は以下の通りです。
  • Arborを使って何をしようとしているか。
  • 具体的にどこが不便で困っているか。
  • 改善するにはどうすればよいか。
注意点:
  • 詳しい状況がわからない要望については答えられません。
  • ユーザー様のプロジェクトの仕様上の固有の問題に対する要望については答えられません。
  • Unityの仕様上の問題に対する要望には答えられません。

This is a forum for requesting Arbor developers.
The items required for the request are as follows.
  • What are you trying to do with Arbor?
  • Specifically, where are you inconvenient and in trouble?
  • What should I do to improve?
Attention point:
  • We can not answer requests that do not know the detailed situation.
  • We can not answer your request for specific problems in the specification of your project.
  • We can not answer your request on Unity's specification issues.

Forum rules
要望に必要な項目は以下の通りです。
  • Arborを使って何をしようとしているか。
  • 具体的にどこが不便で困っているか。
  • 改善するにはどうすればよいか。
注意点:
  • 詳しい状況がわからない要望については答えられません。
  • ユーザー様のプロジェクトの仕様上の固有の問題に対する要望については答えられません。
  • Unityの仕様上の問題に対する要望には答えられません。

The items required for the request are as follows.
  • What are you trying to do with Arbor?
  • Specifically, where are you inconvenient and in trouble?
  • What should I do to improve?
Attention point:
  • We can not answer requests that do not know the detailed situation.
  • We can not answer your request for specific problems in the specification of your project.
  • We can not answer your request on Unity's specification issues.
tonkotu
Posts: 1
Joined: 2020/05/12 02:30

NodeGraph等のInstantiateの負荷について

Post by tonkotu » 2020/05/12 02:53

いつもお世話になっております。
非常に便利に使わせていただいております。

早速、件名の内容についてですが、
主にReflectionによるFieldを使ってシリアライズをしている箇所になるのでが、
例として NodeGraph.cs の以下のメソッドについて
```
void ISerializationCallbackReceiver.OnAfterDeserialize()
{
RegisterNodes();
}

void RegisterNodes()
{
ClearNodes();

EachField<Node>.Find(this, this.GetType(), (node) =>
{
RegisterNode(node);
});
}
```
これらの処理が、Instantiate時に高負荷となっておりかなりのボトルネックとなっております。
どうにかReflectionを使用しないように手を加えられないか、コードをトレースもしてみましたが、
方法が浮かばず、フォーラムにて相談させていただいた次第です。

私ごとではありますが、プロジェクトの終盤を迎え、取り急ぎ解決したい問題でもありまして、
こちらどうにか回避する策などがあればご教授いただけないでしょうか?
よろしくお願いいたします。

User avatar
caitsithware
管理人
Posts: 358
Joined: 2015/08/17 12:41

Re: NodeGraph等のInstantiateの負荷について

Post by caitsithware » 2020/05/12 04:10

ご要望ありがとうございます。

NodeGraph生成時における負荷についてですね。

ObjectPoolによる事前生成

まず、ゲーム途中に生成が頻発し高負荷になってしまうのが問題の場合、ObjectPoolingを使用して必要な数を予め生成しておきオブジェクトを使いまわす方法があります。

主に以下の流れでObjectPoolingを使用します。
  1. AdvancedPoolingでGameObjectを必要数プールする。
    生成には若干時間がかかるため、生成完了まで待つReadyリンクより遷移する。
  2. InstantiateGameObjectのUse Poolにチェックを入れて生成する。
  3. 不要になったらDestroyGameObjectで破棄する(この時プール可能であればプールへ戻される)
注意点としては、再利用された場合にコンポーネントの各フィールドやプロパティは以前のままとなっています。
もし残ってしまうと困る場合は、削除前に初期値に戻すなどの処理も行ってください。
※自動的に初期値に戻す対応を行うとなるとこちらもReflectionを使用せざるを得なくなりObjectPoolingによる負荷軽減の意味がなくなってしまうため、各自対応をお願いいたします。

自作するスクリプトでもArbor内のObjectPooling機能を使用する場合は、ObjectPooling名前空間に各種クラスがありますので参考にしてみてください。

RegisterNodesを各グラフでReflectionを使用せずに行う

ArborFSMInternalBehaviourTreeInternalにNodeGraph.RegisterNodes()を移植し、各ノードリストを直接登録することでReflection使用を回避する方法です。

例えば以下のような移植の流れとなります。
  1. NodeGraphクラスにOnRegisterNodes()を追加(中身は不要)。

    Code: Select all

    protected virtual void OnRegisterNodes()
    {
    }
    
  2. NodeGraph.RegisterNodes()を以下のように変更。

    Code: Select all

    void RegisterNodes()
    {
    	ClearNodes();
    
    	foreach (var node in _Calculators)
    	{
    		RegisterNode(node);
    	}
    
    	foreach (var node in _Comments)
    	{
    		RegisterNode(node);
    	}
    
    	foreach (var node in _Groups)
    	{
    		RegisterNode(node);
    	}
    
    	for (int i = 0; i < _DataBranchRerouteNodes.count; i++)
    	{
    		RegisterNode(_DataBranchRerouteNodes[i]);
    	}
    
    	OnRegisterNodes();
    }
    
  3. ArborFSMInternalクラスにOnRegisterNodes()追加。

    Code: Select all

    protected override void OnRegisterNodes()
    {
    	foreach (var node in _States)
    	{
    		RegisterNode(node);
    	}
    
    	for (int i = 0; i < _StateLinkRerouteNodes.count; i++)
    	{
    		RegisterNode(_StateLinkRerouteNodes[i]);
    	}
    }
    
  4. BehaviourTreeInternalクラスにOnRegisterNodes()追加。

    Code: Select all

    protected override void OnRegisterNodes()
    {
    	RegisterNode(_RootNode);
    
    	for (int i = 0; i < _CompositeNodes.count; i++)
    	{
    		RegisterNode(_CompositeNodes[i]);
    	}
    
    	for (int i = 0; i < _ActionNodes.count; i++)
    	{
    		RegisterNode(_ActionNodes[i]);
    	}
    }
    
※もしforeachの使用も削減する方針でしたら、forへの置き換えはご自由に行ってください。

変更箇所が多岐にわたる点および対象ファイルがシステムの根幹の部分になるため、変更後のファイルの暫定的な配布は行っておりません。
上記移植の流れを参考に変更していただくようお願いいたします。

この変更は、今後の更新で対応する方向で検討いたします。

その他、Reflectionの使用について

今から全体的にReflectionの使用をほぼ禁止とすると、ユーザー様の作成するスクリプトにも変更が必要になってしまうような大幅な仕様変更となってしまうため、現状でReflectionの使用を削減するのは難しいです。
よって現状では全体的なReflection使用削減を行う予定はございません。
ご理解のほどよろしくお願いいたします。

Instantiate時の負荷について

そもそものUnityの仕様としてInstantiate自体も高負荷であるため、頻繁に呼び出すようなことがある場合は、ObjectPoolingを使用するようにお願いいたします。
とはいえ、Arbor内部の処理負荷も高くなっているのも事実であるため、可能な限り負荷軽減できないかも今後の課題として検討いたします。


取り急ぎ、まず全体としてObjectPoolingに対応できそうかやRegisterNodesの変更でどうなるかご検証ください。

以上となります。
ご不便おかけして申し訳ございませんがよろしくお願いいたします。

Post Reply