SerializeReferenceのパフォーマンス検証

SerializeReferenceアトリビュートを付けた参照とScriptableObjectの参照の読み込みはどちらが高速なのか検証してみました。

実験に使用したクラスは以下の通りです。

class A1 : ScriptableObject
{
  public A2[] Children;
}

class A2 : ScriptableObject
{
  public Matrix4x4 Matrix;
}

class B1 : ScriptableObject
{
  [SerializeReference]
  public B2[] Children;
}

[Serializable]
class B2
{
  public Matrix4x4 Matrix;
}

A1(B1) の Children には A2(B2) のインスタンスが100個入っています。 A1(B1) をアセットバンドル化し、Load / Unload を10000回行いました。

環境は 2020.3.25f1 Win64 IL2CPP で下が計測結果になります。

OverviewGC Alloc (A)GC Alloc (B)Time (A)Time (B)
AssetBundle.LoadFromFile2417.201619.79
 File.Open710.61596.70
 File.Read402.12366.84
 File.Close244.46220.33
 Loading.LoadFileHeaders139.12118.60
 AssetBundle.LoadBundleObject17.0215.25
 Loading.MemoryFileSystemOpen40.1039.46
 File.Seek27.6027.07
 Loading.MemoryFileSystemClose12.4512.25
 Loading.MemoryFileSystemRead10.199.16
 Loading.MemoryFileSystemWrite9.148.48
 Loading.ArchiveFileSystemMount7.527.12
 Loading.AwakeFromLoad0.620.59
 Loading.MemoryFileSystemSize0.550.55
LoadObjectsThreaded62.0010.47
 Loading.ReadObjectThreaded2512.57945.16
  GC.Alloc92.2MB84.5MB47.1744.04
  GC.Collect92.2MB84.5MB307.6925.51
  AwakeFromLoadThreaded31.231.44
  AsyncReadManager.SyncRequest4.413.06
  Loading.FindInThreadedActivationQueue103.690.95
  APIUpdaterRuntimeHelpers.GetMovedFromAttributeDataForType9.0KB0.93
  GC.Resize0.000.00
IntegrateAllThreadedObjects246.3039.10
Loading.ArchiveFileSystemClose8.016.86
Loading.MemoryFileSystemClose7.617.05
Loading.LoadRemainingPreallocatedObjects8.972.96
Loading.MakeObjectUnpersistent1.771.55
GC.Alloc234.4KB234.4KB1.531.27
合計7381.654132.54

SerializeReferenceを使用した場合だけAPIUpdaterRuntimeHelperのメソッドが呼ばれていました。実装を見るとMovedFromアトリビュート用の処理のようです。

https://github.com/Unity-Technologies/UnityCsReference/blob/master/Runtime/Scripting/APIUpdating/APIUpdaterRuntimeHelpers.cs#L20

構造によって変わってくる部分があるかもしれませんが、今回のテストではSerializeReferenceを使用した方が速いという結果になりました。ScriptableObjectを大量に含むアセットを作る場合はSerializeReferenceを利用しても良さそうです。