私はこの記事を読んでいて、この人は、データ指向のデザインとOOPを組み合わせることで誰もがどのように大きな利益を得ることができるかについて話し続けました。ただし、コードサンプルは表示されません。
私はこれをグーグルで調べたところ、コードサンプルはもちろんのこと、これが何であるかについての実際の情報を見つけることができませんでした。誰かがこの用語に精通しており、例を提供できますか?これは別の言葉かもしれませんか?
私はこの記事を読んでいて、この人は、データ指向のデザインとOOPを組み合わせることで誰もがどのように大きな利益を得ることができるかについて話し続けました。ただし、コードサンプルは表示されません。
私はこれをグーグルで調べたところ、コードサンプルはもちろんのこと、これが何であるかについての実際の情報を見つけることができませんでした。誰かがこの用語に精通しており、例を提供できますか?これは別の言葉かもしれませんか?
回答:
まず、これをデータ駆動型設計と混同しないでください。
データ指向設計についての私の理解は、効率的な処理のためにデータを編成することについてです。特にキャッシュミスなどに関しては、データドリブンデザインとは、プログラムの多くの動作をデータに制御させることです(Andrew Keithの回答で非常によく説明されています)。
アプリケーションに、色、半径、弾力、位置などのプロパティを持つボールオブジェクトがあるとします。
オブジェクト指向アプローチ
OOPでは、ボールを次のように記述します。
class Ball {
Point position;
Color color;
double radius;
void draw();
};
そして、次のようなボールのコレクションを作成します。
vector<Ball> balls;
データ指向のアプローチ
ただし、データ指向設計では、次のようなコードを書く可能性が高くなります。
class Balls {
vector<Point> position;
vector<Color> color;
vector<double> radius;
void draw();
};
ご覧のとおり、1つのボールを表す単一のユニットはもうありません。Ballオブジェクトは暗黙的にのみ存在します。
これには、パフォーマンスに関して多くの利点があります。通常、多くのボールを同時に操作したいと考えています。ハードウェアは通常、効率的に動作するためにメモリの大きな連続したチャンクを必要とします。
次に、ballsプロパティの一部にのみ影響する操作を実行する場合があります。たとえば、すべてのボールの色をさまざまな方法で組み合わせる場合は、キャッシュに色情報のみを含める必要があります。ただし、すべてのボールプロパティが1つのユニットに格納されている場合、ボールの他のすべてのプロパティも同様に取得します。あなたはそれらを必要としませんが。
キャッシュの使用例
各ボールが64バイトを使用し、1ポイントが4バイトを使用するとします。キャッシュスロットは、例えば、64バイトもかかります。10個のボールの位置を更新する場合は、10 * 64 = 640バイトのメモリをキャッシュに取り込み、10回のキャッシュミスを取得する必要があります。ただし、ボールの位置を個別のユニットとして処理できる場合、4 * 10 = 40バイトしかかかりません。これは1つのキャッシュフェッチに収まります。したがって、10個のボールすべてを更新するために1回のキャッシュミスしか得られません。これらの数値は任意です-キャッシュブロックが大きいと思います。
ただし、メモリレイアウトがキャッシュヒットに深刻な影響を与え、パフォーマンスにどのように影響するかを示しています。これは、CPUとRAMの速度の違いが大きくなるにつれて、ますます重要になります。
メモリのレイアウト方法
ボールの例では、通常、通常のアプリでは複数の変数に一緒にアクセスする可能性が高いため、問題を大幅に簡略化しました。たとえば、位置と半径はおそらく一緒に頻繁に使用されます。次に、構造は次のようになります。
class Body {
Point position;
double radius;
};
class Balls {
vector<Body> bodies;
vector<Color> color;
void draw();
};
これを行う必要があるのは、一緒に使用されるデータが別々のアレイに配置される場合、それらがキャッシュ内の同じスロットで競合するリスクがあるためです。したがって、一方をロードすると他方がスローされます。
したがって、オブジェクト指向プログラミングと比較すると、最終的に作成するクラスは、問題のメンタルモデルのエンティティとは関係ありません。データはデータの使用に基づいてまとめられるため、データ指向の設計でクラスに付ける適切な名前が常にあるとは限りません。
リレーショナルデータベースとの関係
データ指向設計の背後にある考え方は、リレーショナルデータベースについての考え方と非常に似ています。リレーショナルデータベースの最適化では、キャッシュをより効率的に使用することもできますが、この場合、キャッシュはCPUキャッシュではなく、メモリ内のページです。また、優れたデータベース設計者は、アクセス頻度の低いデータを別のテーブルに分割する可能性が高く、数列しか使用されていないテーブルを大量に作成するのではなく、また、ディスク上の複数の場所からデータにアクセスする必要がないように、いくつかのテーブルを非正規化することを選択する場合もあります。データ指向設計の場合と同様に、これらの選択は、データアクセスパターンとは何か、パフォーマンスのボトルネックはどこにあるかを調べることによって行われます。
struct balls {vector<vec3> pos; vector<vec3> velocity;}
速度ベクトルと位置ベクトルの間で前後に移動するため、各ボールの位置を実際に更新していない構造があるとします(最新のマシンとキャッシュラインなどすべて、これはイラストも)
struct balls { vector<color> colors; vector<body> bodies; /* contains position and velocity */ }
ます。
Mike Actonは最近、データ指向設計について公開講演しました。
私の基本的な要約は次のとおりです。パフォーマンスが必要な場合は、データフローについて考え、ねじ込み、最適化する可能性が最も高いストレージレイヤーを見つけます。マイクはリアルタイムで行うため、L2キャッシュミスに焦点を当てていますが、データベース(ディスク読み取り)やWeb(HTTPリクエスト)にも同じことが当てはまると思います。これはシステムプログラミングを行うのに便利な方法だと思います。
アルゴリズムや時間の複雑さについて考えるのをやめないことに注意してください。それは、マッドCSスキルでターゲットにする必要がある最も高価な操作タイプを理解することに集中するだけです。
Noelがゲーム開発で直面している特定のニーズについて具体的に話していることを指摘したいだけです。リアルタイムソフトシミュレーションを実行している他のセクターがこれから利益を得ると思いますが、一般的なビジネスアプリケーションに顕著な改善を示すテクニックになるとは思えません。この設定は、パフォーマンスの最後のすべてのビットが基盤となるハードウェアから確実に排除されるようにするためのものです。
データ指向設計とは、アプリケーションのロジックが、手続き型アルゴリズムではなく、データセットで構築される設計です。例えば
手続き型アプローチ。
int animation; // this value is the animation index
if(animation == 0)
PerformMoveForward();
else if(animation == 1)
PerformMoveBack();
.... // etc
データ設計アプローチ
typedef struct
{
int Index;
void (*Perform)();
}AnimationIndice;
// build my animation dictionary
AnimationIndice AnimationIndices[] =
{
{ 0,PerformMoveForward }
{ 1,PerformMoveBack }
}
// when its time to run, i use my dictionary to find my logic
int animation; // this value is the animation index
AnimationIndices[animation].Perform();
このようなデータ設計は、アプリケーションのロジックを構築するためのデータの使用を促進します。特に、アニメーションやその他の要因に基づいて何千ものロジックパスを持つ可能性のあるビデオゲームでは、管理が容易です。