ScriptableObject とそれ以外のパフォーマンス比較

調査内容

下記のような ScriptableObject や struct に対しデータの読み込み速度を測ってみた。

public class TestScriptableObject : ScriptableObject {
  public string[] stringValue;
  public int[] intValue;
  public float[] floatValue;
}
public struct TestStruct{
  public string[] stringValue;
  public int[] intValue;
  public float[] floatValue;
}

Unity のバージョンは 2017.3.1f1。 デバイスは ZenPad3 8.0 Z581KL 。

結果

// ハードコーディング
// PFS=36
for ( var i = 0; i < 2000; ++i ){
  var data = ScriptableObject.CreateInstance< TestScriptableObject >();
  data.stringValue = new string [ 10 ];
  for ( var n = 0; n < data.stringValue.Length; ++n ) data.stringValue[ n ] = "Hello";
  data.intValue = new int [ 10 ];
  for ( var n = 0; n < data.intValue.Length; ++n ) data.intValue[ n ] = 1;
  data.floatValue = new float [ 10 ];
  for ( var n = 0; n < data.floatValue.Length; ++n ) data.floatValue[ n ] = 9.8f;
}
// ScriptableObject
// PFS=14
for ( var n = 0; n < 2000; ++n ){
  var data = Resources.Load( "ScriptableObject/TestScriptableObject" ) as TestScriptableObject;
  Resources.UnloadAsset( data );
}
// Text
// PFS=8
for ( var i = 0; i < 2000; ++i ){
  var text = Resources.Load( "ScriptableObject/TestText" ) as TextAsset;
  var separator = new char []{ '\n' };
  var strings = text.text.Split( separator );
  Resources.UnloadAsset( text );
  var data = new TestStruct();
  var index = 0;
  var length = 0;
  length = int.Parse( strings[ index++ ] );
  data.stringValue = new string [ length ];
  for ( var n = 0; n < data.stringValue.Length; ++n ) data.stringValue[ n ] = strings[ index++ ];
  length = int.Parse( strings[ index++ ] );
  data.intValue = new int [ length ];
  for ( var n = 0; n < data.intValue.Length; ++n ) data.intValue[ n ] = int.Parse( strings[ index++ ] );
  length = int.Parse( strings[ index++ ] );
  data.floatValue = new float [ length ];
  for ( var n = 0; n < data.floatValue.Length; ++n ) data.floatValue[ n ] = float.Parse( strings[ index++ ] );
}
// Binary
// PFS=13
for ( var i = 0; i < 2000; ++i ){
  var text = Resources.Load( "ScriptableObject/TestBin" ) as TextAsset;
  using( var fos = new MemoryStream( text.bytes ) ){
    using ( var reader = new BinaryReader( fos ) ){
      var data = new TestStruct();
      var length = 0;
      length = reader.ReadInt32();
      data.stringValue = new string [ length ];
      for ( var n = 0; n < data.stringValue.Length; ++n ) data.stringValue[ n ] = reader.ReadString();
      length = reader.ReadInt32();
      data.intValue = new int [ length ];
      for ( var n = 0; n < data.intValue.Length; ++n ) data.intValue[ n ] = reader.ReadInt32();
      length = reader.ReadInt32();
      data.floatValue = new float [ length ];
      for ( var n = 0; n < data.floatValue.Length; ++n ) data.floatValue[ n ] = reader.ReadSingle();
    }
  }
  Resources.UnloadAsset( text );
}

まとめ

1.ハードコーディング(FPS=36)
2.ScriptableObject(FPS=14)
3.バイナリ読み込み(FPS=13)
4.テキスト読み込み(FPS=8)

実測してみると ScriptableObject が優秀だと分かった。

バイナリ読み込みは速度が出るけど自力でパースする手間がある。
テキスト読み込みはちょっと遅い。

開発初期は XML, JSON, CSV でデータを用意し、ある程度データ形式が固まってきたらパフォーマンスが気になるところを ScriptableObject に変えていくとかが現実的な使い方かもしれない。

XML, JSON, CSV は Editor ディレクトリに入れてエディタ拡張で ScriptableObject に変換。

使用したプロジェクト

github.com