Version: 2019.1
C# Job System の安全システム
ジョブの作成

NativeContainer

データをコピーする 安全システム による処理の欠点は、各コピー内のジョブの結果も単独化してしまうことです。この制限を解決するには、NativeContainer と呼ばれる共有メモリに結果を保存する必要があります。

NativeContainer とは

NativeContainer はマネージ型で、比較的安全な C# ラッパーをネイティブメモリに提供します。これには、アンマネージの割り当てへのポインターが含まれます。Unity C# Job System で使用する場合、NativeContainer を使用すると、ジョブはコピーを使って作業するのではなく、メインスレッドと共有するデータにアクセスできるようになります。

どのようなタイプの NativeContainer が利用可能か

Unity には、NativeArray と呼ばれる NativeContainer が搭載されています。NativeArrayNativeSlice で操作して、NativeArray の特定の位置から特定の長さのサブセットを取得することもできます。

注意 Entity Component System (ECS) パッケージは、Unity.Collections 名前空間を拡張して、他の NativeContainer 属性のある型が含まれています。

  • NativeList - サイズ変更可能な NativeArray
  • NativeHashMap - キーと値のペア
  • NativeMultiHashMap - 各キーに複数の値
  • NativeQueue - 先入れ先出し (FIFO) キュー

NativeContainer と安全システム

セーフティシステムはすべての NativeContainer タイプに組み込まれています。 NativeContainer へのすべての読み取りと書き込みを追跡します。

注意 NativeContainer タイプのすべての安全チェック (例えば、範囲外チェック、割り当て解除チェック、競合状態チェック) は、Unity の エディター再生モード でのみ使用可能です。

この安全システムには DisposeSentinelAtomicSafetyHandle が含まれます。 DisposeSentinel はメモリリークを検出し、正しくメモリが解放されていない場合はエラーを返します。メモリリークエラーは、リークが発生してからかなり後に送信されます。

AtomicSafetyHandle を使用すると、コードの NativeContainer の所有権を移動します。例えば、2 つのスケジュールされたジョブが同じ NativeArray に書き込みを行なうと、安全システムは例外を発生させ、問題の原因と解決方法を説明する明確なエラーメッセージを示します。安全システムは、違反のジョブがスケジュールされたときにこの例外をスローします。

この場合、依存関係を持つジョブをスケジュールに組み込めます。最初のジョブは NativeContainer に書き込むことができ、実行が終了すると、次のジョブは同じ NativeContainer から安全に読み取り、または書き込みすることができます。読み取りと書き込みの制限は、メインスレッドからデータにアクセスするときにも適用されます。安全システムでは、複数のジョブが並列で同じデータから読み取ることができます。

デフォルトでは、NativeContainer へアクセスできるジョブは、読み取りと書き込みの両方のアクセスが可能です。この設定はパフォーマンスを遅くする可能性があります。C# Job System では、NativeContainer への書き込みアクセス権を持つジョブを、NativeContainer への書き込みアクセス権を持つ他のジョブと同時にスケジュールすることはできません。

ジョブが NativeContainer への書き込みを必要としない場合は、以下のように NativeContainer[ReadOnly] 属性を設定します。

[ReadOnly]
public NativeArray<int> input;

上の例では、読み取り専用アクセス権を持つジョブと、NativeContainer への読み取り専用アクセス権を持つその他のジョブを同時に実行することができます。

注意 ジョブ内からの静的データへのアクセスに対する保護はありません。静的データにアクセスすると、すべての安全システムを回避し、Unity をクラッシュさせる可能性があります。詳細は、C# Job System のヒントとトラブルシューティング を参照してください。

NativeContainer アロケーター

NativeContainer を作成するときに、必要なメモリ割り当てのタイプを指定する必要があります。割り当てタイプは、ジョブが実行される時間の長さによって異なります。これにより、各状況で最高のパフォーマンスが得られるように割り当てを調整できます。

NativeContainer メモリ割り当てと解放には 3 つの Allocator タイプがあります。NativeContainer をインスタンス化するときに適切なものを指定する必要があります。

  • Allocator.Temp は最も速く割り当てを行えます。これは 1 フレーム以下のスパンのアロケーションで行われます。Temp を使って割り当てを行った NativeContainerをジョブに渡すべきではありません。また、メソッドの呼び出し (MonoBehaviour.Update など) や、その他すべてのネイティブコードからマネージコードへのコールバックから戻る前に Dispose メソッドを呼び出す必要があります。
  • Allocator.TempJobTemp よりもスピードの遅い割り当てですが、Persistent よりも高速です。これは、4 フレーム以内のスパンの割り当てであり、スレッドセーフです。4 フレーム以内で Dispose を行わないと、コンソールはネイティブコードから生成された警告を出力します。ほとんどの小さなジョブはこの割り当てタイプを使用します。
  • Allocator.Persistent は最も遅い割り当てですが、好きなだけ長く、必要な場合は、アプリケーションの生存期間を通して使用できます。これは、malloc (英語) への直接呼び出しのためのラッパーです。長いジョブはこの割り当てタイプを使用できます。パフォーマンスが重要な場合は Persistent を使用しないでください。

NativeArray<float> result = new NativeArray<float>(1, Allocator.TempJob);

ノート 上の例の数字 1 は、 NativeArray のサイズを示しています。この場合、配列要素は 1 つだけです (result に 1 つのデータしか格納していないため)。


  • 2018–06–15 編集レビュー を行ってパブリッシュされたページ

  • C# Job System は 2018.1 で公開NewIn20181

C# Job System の安全システム
ジョブの作成