Open8
UnityのDOTSの全体像を掴む

参考

DOTSとは
- Data-Oriented Tech Stack
- データ指向アーキテクチャをサポートするUnityの新しい基盤

なぜDOTSなのか
従来のアーキテクチャの問題点
- GC
- 大量のGCが発生することでフレームレートが低下することがある
- 生成されるマシンコードが最適じゃない
- CPUのマルチコアを活用できていない
- Unityは基本メインスレッドで実行される
- キャッシュフレンドリーじゃない
- ランダムなオブジェクトがメモリ全体に散在する
- コードが抽象化されすぎている
- オブジェクト指向の弊害
- データがメモリ全体に分散
- 実際の作業を殆ど行わないオブジェクトやメソッドが多く発生
- 呼び出しチェーンの複雑化
- 大きなものを分割することで複雑さを分散させるだけで終わる可能性がある
- 推測的な作業は結果的に時間の無駄になることが多い
- オブジェクト指向の弊害

DOTSの構成要素
- Burst
- Job System
- ECS

Burst
C#をLLVM IRに変換してLLVMを用いて高速な機械語を出力するコンパイラ
※LLVM:様々な言語に対応したコンパイラ基盤
※IR:LLVMが扱う中間言語
- C#のサブセットで記述する
- MonoやIL2CPPより最適化されている
- 一般的なC#コードはコンパイルできない
- マネージドオブジェクト(ランタイムやガベージコレクタで管理されているオブジェクト)にアクセスできない
- クラスや配列などが使えない
- Job Systemといっしょに使う
マネージドとアンマネージド
- マネージドメモリ:ランタイムやガベージコレクタで管理されている
- メモリの配置がごちゃごちゃしている
- アンマネージドメモリ:ランタイムやガベージコレクタで管理されていない
- ECSではこっちを利用する
- メモリを自分で解放する必要がある
- オブジェクトではなくアルゴリズムで生存期間を決める
- 実行する前に欠陥に気づける
- structを使う
- classだとマネージドになってしまうため
- 実装は必ずポインタを扱う

Job System
並列処理を行うための機能
- スレッド間の安全性を確保し、依存関係を管理することが大事
- 競合を回避する安全機構がある
- ワーカースレッドで実行することでBurstコンパイラが使える
- そのためにNativeArrayなどアンマネージドなオブジェクトを使わなければならない
- そのうえで並列Jobを使うとジョブを分割して複数スレッドで実行できる
- バッチに分割

ECS(Entity Component System)
データ指向アーキテクチャをUnity用に最適化したもの
Entity・Component・System
- Component:構造体
- Entity:複数のComponentで構成される
- System:実際の処理を持つ
- 各Systemに1フレーム1回呼び出されるOnUpdateメソッドがある
- Systemの働き
- チャンクを選ぶ
- 処理する
- Jobを発行して並列処理
- 入力・出力を決める
アーキタイプ
- 同じComponentのセットを持つEntityはすべて同じアーキタイプで括られる
- EntityにComponentを追加したり、削除したりするとEntityは別のアーキタイプに移される
チャンク
EntityとComponentを入れるメモリのブロック
- チャンクの配列は常に密
- 同じアーキタイプを専用のチャンクにまとめればメモリ上にきれいに並ぶ
- チャンクの大きさは一定で、満杯ならチャンクが追加される
- この形の利点はEntityの効率的なクエリとイテレーションを可能にすること
Bake
サブシーンのゲームオブジェクトからEntityを読み込む処理
- ECSではゲームオブジェクトを管理するのにサブシーンを使用する
- サブシーンはシーンの中にネストされて、編集されるたびにBakeされる
- ランタイムにロードされるのはゲームオブジェクトではなくEntity
- 直接編集するデータとランタイムで読み込まれるデータが1対1である必要がなくなる
- Bake中にデータをいっぱい生成すればランタイムでのコストが減る

導入検討
- 静的な要素が多く含まれる場合
- 環境のレンダリングなど
- 計算負荷の高い挙動を持つ動的要素が多く含まれる場合
- 推論や保守を簡単にしたいとき
※GPUのボトルネックを解消できるわけじゃない