Native Containerざっくりまとめ

前書き

C# Job System と ECS でよく使われる Native Container について軽くまとめました。
ざっくりとした説明なので詳しい情報は調べてみてください。



注意点

2019/12/01 時点で調べた内容です。


なに?

  • 管理されていない割り当てへのポインタが含まれている。
  • Unity C# Job Systemで NativeContainer を使用すると、ジョブはコピーではなくメインスレッドと共有されるデータにアクセスできる。



安全性

  • 安全性チェックは Unity Editor およびPlayモードでのみ使用できる。
    • 安全性チェック
      • 範囲外チェック
      • 割り当て解除チェック
      • 競合状態チェック ...etc
  • メモリを解放し忘れた場合、かなり時間を経過してメモリリークのエラーが起きる。



種類

  • NativeArray
    • 配列
  • NativeSlice
    • NativeArrayから一部のデータを取り出すことができる
  • NativeList
    • 可変長なNativeArray
  • NativeHashMap
    • キーと値のペア
  • NativeMultiHashMap
    • キーごとに複数の値を設定できる
  • NativeQueue



Native Container Allocator

NativeContaine のメモリ割り当てタイプを指定できる。

よく使うのはTemp、 TempJob、 Persistentの3つ。

種類

  • Invalid
    • 無効な割り当て。
  • None
    • 割り当てなし。
  • Temp
    • 自分で解放するする必要がある。
    • そのフレームのみ有効。
  • TempJob
    • C# Job Systemで使う前提の一時割当。NativeArrayにDeallocateOnJobCompletion属性を付けるとJob終了時にNativeArrayを自動で解放してくれる。
    • そのJob中のみ有効。
  • Persistent
    • 永続的な割当。
    • ComponentやComponentSystemのAwake等のタイミングで作って、OnDestroy等で消す。
    • 解放するまで有効。
  • AudioKernel
    • DSPGraphオーディオカーネルに関連付けられた割り当て。

メモリ確保のコスト

高い

Persistent
TempJob
Temp

低い

使い方

var position = new NativeArray<Vector3>(500, Allocator.Persistent);



NativeArray

C# Job Systemでよく使うであろう NativeArray について説明します。


なに?

Unityが用意した配列用のAPI
アンマネージドなメモリ領域をマネージドコードに公開している。


恩恵

高速に処理が行える。


なぜ高速に処理ができるのか?

Native Arrayはメモリ配置が連続しており、アクセスが高速になる。
 → C#のArrayはメモリ配置が連続していない。


特徴

  • Allocatorを自分で決める必要がある
  • 使えるのは構造体のみ
  • 要素は増やせない


使い方

NativeArray<Type>(size, Allocator);
  • Type
    • プリミティブな型かstruct型のみが指定可能。class型は指定不可。
  • size
  • Allocator
    • メモリの生存期間。Native Container Allocatorで指定。


void Update()
{
    // 入力バッファを用意
    var commands = new NativeArray<RaycastCommand>(target.Length, Allocator.TempJob)
    var results = new NativeArray<RaycastHit>(targets.Length, Allocator.Temp);
    
    // 入力バッファを埋める
    for(int i = 0; i < targets.Legnth; ++i)
    {
        var targetPosition = targets[i].position;
        var direction = Vector3.down;
        var command = new RaycastCommand(targetPosition, direction);
        commands[i] = command;
    }
    
    // Jobを発行して終了を待つ
    RaycastCommand.ScheduleBatch(commands, results, 20).Complete();
    commands.Dispose();
    
    // 結果をバッファから受け取って何かを反映させる
    for(int i = 0; i < targets.Length; ++i)
    {
        if(velocity[i] < 0 && results[i].distance < 0.5f)
            velocity[i] = 2;
        velocity[i] -= 0.098f;
    }
    results.Dispose();
    
    for(int i = 0; i < targets.Length; ++i)
    {
        targets[i].localPosition += Vector3.up * velocity[i];
    }
}



Native Slice

これだけ特殊なので紹介します。

なに?

NativeArray の一部分のデータを取り出すことができる。

使い方

NativeSlice<Type>(NativeArray, Index, Length);
  • Type
    • 取り出す要素の型。
  • NativeArray
    • 取り出す対象のNativeArray。
  • Index
    • 取り出したい先頭要素の添え字。
  • Length
    • 取り出したい要素数


最後に

C# Job System で必須な Native Container をざっくりまとめました。
メモリを解放し忘れても警告が出るので安心ですね。