データ指向設計
Motivation
今日においては、CPUの処理速度に対して、メインメモリのIOは非常に鈍足。
CPUの処理の多くがメモリからのIO待ちになっているのが現状である
メインメモリ→L2 キャッシュ→L1 キャッシュ→CPU Coreというデータフローが起きるが、キャッシュに対してメモリが遅い
キャッシュラインを64bitと仮定して、一度のデータ読み込みの時に必要なデータ量が10bit程度だとすると、54bit分無駄にしている(キャッシュミス)
なので最適なメモリ配置を頑張ると、メモリバンドが最適化され、キャッシュミスが減り、メモリへの読み込み処理が減らせる
Solution
オブジェクト指向のように、データと処理の組み合わせ(メンバ変数とメンバ関数)という考え方ではなく、データと処理をそれぞれ分けて考える。
木構造的なデータ構造になるのではなく、データ自体はテーブル構造にする
つまり同じ型のデータを連続したメモリ配置にすることによって、Motivation を達成する
具体的な実装としては、データを保持するための構造体(Component)を高い粒度で用意し、それらデータに対してどのような処理をするのかを関数(Component System)として別に記述する。
各関数はその処理を行うために必要なデータセット(Entity)を宣言しておき、内部システムは保持しているデータからEntityに適するデータセットを作り出すことが可能な場合は、その関数を呼び出す。
これはParticle Systemと非常に類似している。
Particle SystemのEmit処理をしている部分がComponent System、Particle Systemに搭載されている各パラメーターがComponent、Particle毎に異なるMaterialやTextureなども含めた実際にEmitするために必要なデータ群がEntityという形である。
Advantage
並列化
CPUが処理をする瞬間においては、データと処理が1対1で紐づくので、入力データ→関数→出力データというシンプルな流れになるので、並列化が用意となる
キャッシュ効率とパフォーマンス
キャッシュ効率の良いメモリアクセス、つまり最適なメモリ配置によってもたらされるメモリバンドの最適化、それによってキャッシュミスが減りメモリのIOが減るので、パフォーマンスが向上する
モジュール性
関数とデータが分離しているので、関数だけを別に流用するのは容易となる
データフォーマットさえ合っていれば、どのようなデータであるかは問題ではないからである(逆に問題となるのであれば、関数をより小さくするべきかもしれない)
テスト性
オブジェクト指向のテストは正直ダルい
データ指向設計においては、必要な一連の入力データを用意した後は関数を呼び出すだけなのでシンプルとなる
さらに入力⇔出力の紐づけが明確となる
Disadvantage
直感的ではない
オブジェクト指向に慣れ親しんでいる人ほど感覚とのズレを感じるかもしれない
データと処理を分割して考えられる思考力が必要となる
プログラムが肥大化しやすい
データと処理を細かい単位で分けてしまうので、必然的に肥大化しやすい
この場合の肥大化とは、プログラムの規模ではなく、構造体の数や関数の数のことを指している
DRYの原則に反しやすくなり、プロジェクト全体を見渡せる広い視野が求められるようになる