Performance

When passing values in dataflow, there is a risk of performance degradation due to GC Alloc in boxing.

The conditions under which performance degradation due to boxing can occur are as follows

  • The type of data to be passed is the value type.
  • A type that is not registered in ValueMediator.
  • It is passed through a generic slot such as OutputSlotAny or OutputSlotTypable.
    (This also applies to type-specified scripts such as built-in List-related scripts.)
  • The object version of the input/output method is used.
  • If you are using the following built-in scripts, boxing cannot be avoided because they deal directly with object types

Use a type-specific slot class

Avoid using slots of unnecessary generic types, for example, use OutputSlotInt if you are sure that only int type will be used.

Avoid boxing with ValueMediator

If you use OutputSlotAny or OutputSlotTypable, you can reduce the boxing by using ValueMediator.

Registering ValueMediator

Boxing can be reduced by registering the value type in ValueMediator.

As an example, the code to register the DataFlowExampleData structure in the Arbor.Example namespace to the ValueMediator is as follows

 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>();
	}
}

Use the generic version of the method for value input and output

If you use the object version of the method, such as OutputSlotAny.SetValue(object value), it will be boxed at that point, so use a generic method such as SetValue<T>(T value) instead.

Example of output slot side

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

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

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

Example of the input slot side

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

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

	// Use this for enter state
	public override void OnStateBegin()
	{
		Arbor.Example.DataFlowExampleData data = new Arbor.Example.DataFlowExampleData();
		if (input.GetValue<Arbor.Example.DataFlowExampleData>(ref data))
		{
			Debug.LogFormat("stringValue : {0}\nintValue : {1}", data.stringValue, data.intValue.ToString());
		}
	}
}

Types pre-registered in ValueMediator

  • sbyte
  • byte
  • short
  • ushort
  • int
  • uint
  • long
  • ulong
  • char
  • float
  • double
  • 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