ドキュメント

Unity Engine

Unity Engine

スレッドセーフタイプ

Understand Unity's custom thread-safe type, NativeContainer.
読み終わるまでの所要時間 10 分最終更新 1ヶ月前

ジョブシステムは、Burst コンパイラー と併用するのが最も効果的です。Burst はマネージオブジェクトをサポートしていないので、ジョブのデータにアクセスするにはアンマネージタイプを使う必要があるためです。これには blittable 型 を使うか、Unity のビルトインの
NativeContainer
オブジェクトを使用できます。これらは、ネイティブメモリのためのスレッドセーフな C# ラッパーです。
NativeContainer
オブジェクトはジョブがコピーで作業するのではなく、メインスレッド と共有されるデータにアクセスすることを可能にします。

NativeContainer の型

Unity.Collections
名前空間には、以下のビルトイン
NativeContainer
オブジェクトがあります。
  • NativeArray
    : マネージコードにネイティブメモリのバッファを公開するアンマネージ配列
  • NativeSlice
    :
    NativeArray
    の特定の位置から特定の長さのサブセットを取得します。
注意Collections パッケージ には、追加の
NativeContainer
が含まれています。追加の型の完全なリストについては、Collections ドキュメントの Collection 型 を参照してください。

読み書きアクセス

デフォルトでは、
NativeContainer
インスタンスへアクセスできるジョブは、読み取りと書き込みの両方のアクセスが可能です。この設定はパフォーマンスを遅くする可能性があります。これは、ジョブシステムでは、
NativeContainer
インスタンスへの書き込みアクセス権を持つジョブを、そのインスタンスへの書き込みアクセス権を持つ他のジョブと同時にスケジュールすることができないためです。
しかし、ジョブが
NativeContainer
インスタンスに書き込む必要がない場合は、以下のように
NativeContainer
に
[ReadOnly]
属性を設定することができます。
[ReadOnly]public NativeArray<int> input;
上の例では、読み取り専用アクセス権を持つジョブと、同様に最初の
NativeArray
への読み取り専用アクセス権を持つその他のジョブを同時に実行することができます。

メモリ割り当て

NativeContainer 
インスタンスを作成するときに、必要なメモリ割り当てタイプを指定する必要があります。使用する割り当てタイプは、ネイティブコンテナを使用可能な状態にしておく時間によって決まります。これにより、各状況で最高のパフォーマンスが得られるように割り当てを調整できます。
NativeContainer
メモリ割り当てと解放には 3 つの Allocator タイプがあります。
NativeContainer
インスタンスをインスタンス化するときに適切なものを指定する必要があります。
  • Allocator.Temp
    : 最も高速な割り当てが可能です。生存期間が 1 フレーム以下の割り当てに使ってください。ただし、
    Temp
    を使って、ジョブのメンバーフィールドに保管されている
    NativeContainer
    インスタンスに割り当てを渡すことはできません。
  • Allocator.TempJob
    :
    Temp
    よりもスピードの遅い割り当てですが、
    Persistent
    よりも高速です。これは、4 フレーム以内の生存期間のスレッドセーフな割り当てに使用します。重要:4 フレーム以内でこの割り当てタイプに
    Dispose
    を行わないと、コンソールはネイティブコードから生成された警告を出力します。ほとんどの小さなジョブはこの割り当てタイプを使用します。
  • Allocator.Persistent
    : 最も遅い割り当てですが、好きなだけ長く、必要な場合は、アプリケーションの生存期間を通して使用できます。これは、
    malloc
    への直接呼び出しのためのラッパーです。長いジョブはこの NativeContainer 割り当てタイプを使用できます。パフォーマンスが重要な場合は
    Persistent
    を使用しないでください。
例:
NativeArray<float> result = new NativeArray<float>(1, Allocator.TempJob);
注意上の例の数字 1 は、NativeArray のサイズを示しています。この場合、配列要素は 1 つだけです。理由は、result (結果) に 1 つのデータしか格納しないためです。

NativeContainer と安全システム

安全システム はすべての
NativeContainer
インスタンスに組み込まれています。すべての
NativeContainer
インスタンスへの読み込みや書き込みを追跡し、その情報を使って
NativeContainer
の使用に一定のルールを適用し、複数のジョブやスレッドにまたがって決定論的な動作を行うようにします。
例えば、2 つの個々にスケジュールされたジョブが同じ
NativeArray
に書き込む場合、どちらのジョブが先に実行されるか予測できないため、これは安全ではありません。つまり、どちらのジョブがもう一方のジョブのデータを上書きするかわからないということです。安全システムは、2 番目のジョブがスケジュールされる際に、理由と問題を解決する方法を説明する明確なエラーメッセージとともに例外をスローします。
同じ
NativeContainer
インスタンスに書き込む 2 つのジョブをスケジュールする場合、依存関係を持つジョブ をスケジュールできます。最初のジョブが
NativeContainer
に書き込み、実行が終了すると、次のジョブが同じ
NativeContainer
を安全に読み書きします。依存関係を導入することで、ジョブが常に一貫した順序で実行され、
NativeContainer
内の結果のデータが決定論的であることが保証されます。
安全システムは、複数のジョブが同じデータを並行して読み取ることを可能にします。
これらの読み取りと書き込みの制限は、メインスレッドからデータにアクセスするときにも適用されます。例えば、NativeContainer に書き込むジョブが完了する前に NativeContainer のコンテンツを読み取ろうとすると、安全システムはエラーをスローします。同様に、NativeContainer への読み取りまたは書き込みを保留しているジョブがまだあるときに NativeContainer に書き込もうとすると、安全システムはエラーをスローします。
また、NativeContainer は
ref return
を実装していないため、
NativeContainer
のコンテンツを直接変更することはできません。例えば、
nativeArray[0]++;
は
var temp = nativeArray[0]; temp++;
と書くのと同じで、
nativeArray
の値は更新されません。
代わりに、インデックスのデータからローカルの一時的なコピーを作成し、そのコピーを変更して、それを元のデータに保存する必要があります。 例:
MyStruct temp = myNativeArray[i];temp.memberVariable = 0;myNativeArray[i] = temp;

追加リソース