私が物事に取り組む方法では、マルチスレッド化は無料で、後から適用するのは比較的簡単です。しかし、私は最初にデータについて考えています。これがすべてのドメインで機能するかどうかはわかりませんが、どのように対処するかについて説明します。
したがって、最初に頻繁に処理されるソフトウェアに必要な最も粗い種類のデータについてです。メッシュ、サウンド、モーション、パーティクルエミッタ、ライト、テクスチャなどのようなゲームの場合。そしてもちろん、メッシュだけにドリルダウンして、それらがどのように表現されるかを考える場合、多くのことを考慮する必要がありますが、ここではそれをスキップします。現在、最も広いアーキテクチャレベルで考えています。
そして、私が最初に考えたのは、「これらすべてのものの表現を統合して、これらすべての種類のものに対して比較的均一なアクセスパターンを実現するにはどうすればよいのか」ということです。そして、私の最初の考えは、空のスペースを再利用するための無料のリストタイプの方法で、すべてのタイプのモノを独自の連続した配列に格納することかもしれません。そして、APIを統一する傾向があるので、たとえば、ライトやテクスチャと同じ種類のコードを使用して、少なくともこれらのコンポーネントがアクセスされる場所と方法まで、メッシュをシリアル化できます。すべてがどのように表現されるかを統一できるほど、それらにアクセスするコードはより統一された形をとる傾向があります。
カッコいい。これで、32ビットインデックスでこれらのことを指すことができ、64ビットポインターの半分のメモリしか使用できなくなります。そして、ねえ、並列ビットセットを関連付けることができれば、交差を線形時間で設定できるようになります。たとえば、すべてのインデックスを作成しているため、データをこれらのいずれかに非常に安価に並列に関連付けることもできます。ああ、そのビットセットは、メモリアクセスパターンを改善するために一連のソートされたインデックスを順番にトラバースするように戻すことができ、単一のループで同じキャッシュラインを複数回リロードする必要がありません。一度に64ビットをテストできます。すべての64ビットが設定されていない場合は、64要素を一度にスキップできます。すべて設定されていれば、一度に処理できます。すべてではなく一部が設定されている場合、FFS命令を使用して、設定されているビットをすばやく判別できます。
しかし、まあ、数万の内の数百のものにデータを関連付けたいだけの場合、それは一種の高価です。代わりに、スパース配列を使用してみましょう。
そして、まあ、すべてが疎な配列に格納され、それらにインデックスが付けられたので、これを永続的なデータ構造にするのは非常に簡単です。
変更されていないものをディープコピーする必要がないため、副作用のないより安価な関数を作成できます。
ここで、ECSエンジンについて学習した後、チートシートは既に与えられていますが、ここで、各タイプのコンポーネントで動作する必要がある広範な機能のタイプについて考えてみましょう。これらを「システム」と呼ぶことができます。「SoundSystem」は「Sound」コンポーネントを処理できます。各システムは、1つ以上のタイプのデータを操作する幅広い機能です。
これにより、特定のコンポーネントタイプについて、通常1つまたは2つのシステムのみがアクセスする多くのケースが残ります。うーん、それは確かにそれがスレッドの安全性を助け、絶対にスレッドの競合を最小限に抑えるように思えます。
さらに、データを均一に渡す方法を検討します。のような代わりに:
for each thing:
play with it
cuddle it
kill it
私はそれを複数のより単純なパスに分割しようとしています:
for each thing:
play with it
for each thing:
cuddle it
for each thing:
kill it
そのため、次の均質な据え置き遅延処理のために中間状態を保存する必要がある場合がありますが、各ループのロジックがよりシンプルで均一であることを知っているため、コードの維持と推論に本当に役立ちます。そしてねえ、それはそれがスレッドの安全性を簡素化し、スレッドの競合を減らすように思えます。
そして、スレッドの安全性と正確さについて自信を持って並列化することが本当に簡単なアーキテクチャが見つかるまで、このように続けますが、最初はすべて、データ表現を統一し、より予測可能なメモリアクセスパターンを持ち、メモリ使用量、制御フローをより均一なパスに簡略化、非常に高価なディープコピーコストを発生させずに副作用を引き起こすシステム内の関数の数を減らし、APIを統一します。
これらすべてを組み合わせると、並行性に非常に配慮した設計に偶然遭遇した共有状態の量を最小限に抑えるシステムになってしまう傾向があります。また、状態を共有する必要がある場合、スレッドのトラフィックジャムを引き起こさずに同期を使用する方が安く、さらに、表現を統合する中央データ構造で処理できることが多いため、多くの競合がないことがよくあります。システム内のすべてのものの数百の異なる場所にスレッド同期を適用する必要はなく、ほんの一握りです。
次に、メッシュなどのより複雑なコンポーネントの1つにドリルダウンするとき、最初にデータについて考えることから始めて、それを設計する同じプロセスを繰り返します。そうすれば、1つのメッシュの処理を簡単に並列化することさえできるかもしれませんが、すでに構築した幅広いアーキテクチャ設計により、複数のメッシュの処理を並列化できます。