パフォーマンス

データフローで値の受け渡しをする場合、ボックス化でのGC Allocなどによりパフォーマンスが低下する恐れがあります。

ボックス化の発生条件

以下のいずれかに該当する場合にボックス化が発生します。

ボックス化回避方法

型固有のスロットクラスを使用する

例えばint型のみの使用が確定している場合にはOutputSlotIntを使用するなど、不必要な汎用タイプのスロット使用は避けてください。

ValueMediatorによるボックス化回避

OutputSlotAnyOutputSlotTypableなどを使用する場合、ValueMediatorを使用することでボックス化が削減できます。

ただし、ValueMediatorを利用した場合でも初回出力時は格納用インスタンス生成のためにGC Allocが発生します。
2回目以降の出力の際に、作成しておいた格納用インスタンスを使いまわすようにすることで、ボックス化を回避しています。

ValueMediatorの登録

ValueMediatorに値型を登録しておくことで、ボックス化が削減できます。

  • ValueMediatorInitializeOnLoadMethod
    staticメソッドにValueMediatorInitializeOnLoadMethod属性を付けるとValueMediatorが初期化される際に呼ばれるようになります。
  • ValueMediator.Register
    値型をValueMediatorに登録します。
  • ValueMediator.RegisterEnum
    列挙型をValueMediatorに登録します。
    列挙型は扱いが内部的に特殊であるため、Registerメソッドの代わりにこちらを使用してください。

例として、Arbor.Example名前空間にあるDataFlowExampleData構造体をValueMediatorに登録するコードは以下のようになります。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
using Arbor.ValueFlow;

static class ExampleRegisterValueMediator
{
	[ValueMediatorInitializeOnLoadMethod]
	static void OnRegisterValueMeditor()
	{
		ValueMediator.Register<Arbor.Example.DataFlowExampleData>();
	}
}

予め登録済みの型

  • sbyte
  • byte
  • short
  • ushort
  • int
  • uint
  • long
  • ulong
  • char
  • float
  • double
  • bool
  • decimal
  • UnityEngine.Vector2
  • UnityEngine.Vector3
  • UnityEngine.Vector4
  • UnityEngine.Quaternion
  • UnityEngine.Rect
  • UnityEngine.Bounds
  • UnityEngine.Color
  • UnityEngine.Color32
  • UnityEngine.Matrix4x4
  • UnityEngine.Vector2Int
  • UnityEngine.Vector3Int
  • UnityEngine.RectInt
  • UnityEngine.BoundsInt
  • UnityEngine.Ray
  • UnityEngine.Ray2D
  • UnityEngine.RaycastHit
  • UnityEngine.RaycastHit2D

値の入出力にジェネリック版メソッドを使用する

OutputSlotAny.SetValue(object)などのobject版メソッドを使用するとその時点でボックス化してしまうため、代わりにSetValue<T>(T value)などのジェネリックメソッドを使用してください。

出力スロット側の例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
using UnityEngine;
using Arbor;
using Arbor.Example;

public class ExampleSetDataFlow : Calculator
{
	[SlotType(typeof(DataFlowExampleData))]
	public OutputSlotAny output = new OutputSlotAny();

	public override void OnCalculate()
	{
		var data = new DataFlowExampleData()
		{
			stringValue = "abcde",
			intValue = 12345,
		};
		output.SetValue<DataFlowExampleData>(data);
	}
}

入力スロット側の例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
using UnityEngine;
using Arbor;
using Arbor.Example;

[AddComponentMenu("")]
public class ExampleGetDataFlow : StateBehaviour
{
	[SlotType(typeof(DataFlowExampleData))]
	public InputSlotAny input = new InputSlotAny();

	// Use this for enter state
	public override void OnStateBegin()
	{
		if (input.TryGetValue<DataFlowExampleData>(out var data))
		{
			Debug.LogFormat("stringValue : {0}\nintValue : {1}", data.stringValue, data.intValue.ToString());
		}
	}
}

Reflectionを利用しない

DataLinkAttributeInvokeMethodスクリプトなどは便利な反面、Reflectionを介すためボックス化が発生します。

FlexibleField系から入力するようにしたり、固有メソッド呼び出しなどのスクリプトを自作してください。