Performance

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

Conditions for boxing

Boxed will occur if any of the following.

How to avoid boxing

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.

However, even if ValueMediator is used, GC Alloc will occur to create a storage instance at the first output.
By reusing the created storage instance for the second and subsequent outputs, boxing is avoided.

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

Types pre-registered

  • 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

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

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

Do not use Reflection

While DataLinkAttribute and InvokeMethod scripts are convenient, they are boxed because they go through Reflection.

Please input from FlexibleField system or make your own script such as unique method call.