2016-09-28 10 views
6

背景

ゲームエンジン開発では、通常、最適なメモリと計算パフォーマンスのためにデータ指向設計を使用します。Rustでデータ指向設計を実装するにはどうすればよいですか?

パーティクルシステムを例にしましょう。パーティクルシステムで

は、我々は、粒子の多くを有し、各粒子は、等の位置、速度、などのいくつかの属性を有することができる

C++における典型的な実装は次のようになり:

struct Particle { 
    float positionX, positionY, positionZ; 
    float velocityX, velocityY, velocityZ; 
    float mass; 
    // ... 
}; 

struct ParticleSystem { 
    vector<Particle> particles; 
    // ... 
}; 

この実装の1つの問題は、パーティクルの属性が相互にインターリーブされていることです。 このメモリレイアウトはキャッシュに適しておらず、SIMD計算には適していません。今、私たちは一つだけの割り当てがあり、各属性が連続してメモリに常駐

struct ParticleAttribute { 
    size_t size; 
    size_t alignment; 
    const char* semantic; 
}; 

struct ParticleSystem { 
    ParticleSystem(
     size_t numParticles, 
     const ParticleAttribute* attributes, 
     size_t bufferSize) { 
     for (size_t i = 0; i < numAttributes; ++i) { 
      bufferSize += attributes[i].size * numParticles; 
      // Also add paddings to satisfy the alignment requirements. 
     } 
     particleBuffer = malloc(bufferSize); 
    } 

    uint8* getAttribute(const char* semantic) { 
     // Locate the semantic in attributes array. 
     // Compute the offset to the starting address of that attribute. 
    } 

    uint8* particleBuffer;  
}; 

は、代わりに、データ指向設計では、我々は以下のコードを記述します。 は、我々は次のコード書くことができ、粒子をシミュレートするには、次の

symplecticEuler(ps.getAttribute("positionX"), ps.getAttribute("velocityX"), dt); 

getAttribute機能は、特定の属性の開始アドレスを取得します。

質問

これをRustで実装する方法を知りたいと思います。

私はまず、ParticleSystemというクラスを作成します。このクラスは、合計バッファサイズを計算するために数十ParticleAttributeを受け取り、バッファのメモリを割り当てます。 これはRustセーフコードで実行できると思います。

次のステップは、getAttribute関数を実装することです。 は、特定の属性の開始アドレスへの参照を返します。 ここにあなたの助けが必要です。 ローアドレスをオフセットで取得し、目的の型(float *など)にキャストし、その生ポインタをRustの可変参照にラップしますか?

また、私はその参照を介して4つの要素を読み込むためにSIMDライブラリを使用する必要があるので、配列への可変参照への生のポインタをラップする必要がありますと思う。 Rustを使用してこれを達成するにはどうすればよいですか?


更新:属性に関する詳細情報を提供してください。 属性の数と詳細情報は実行時に決定されます。 属性の種類はさまざまですが、基本的なもの(f32、f64、ints、...)のみをサポートする必要があると思います。

答えて

8

これはDODを実装する非常に複雑な方法です。ゲッターの実行時ルックアップを使用するという考えは私をうんざりさせます。事前の属性を知る必要が

struct Particles { 
    x: Vec<f32>, 
    y: Vec<f32>, 
} 

簡易版は、単に属性ごとにメモリ割り当てを持つことです。

その後、すべてのyを取得するためのセナンガンはありません。すでに入力済みです。待っています。これに動的に決定する属性を拡張


はその複雑ではありません。

  • 我々は我々が持っているenumを使用することができ、実行時
  • でルックアップするために与えられた属性をHashMap<String, xxx>を使用することができます単一のValueは、様々な形式をとることができるハッシュマップに格納される(他の解決策は、特性を使用する)。

これは次のようになります。私たちは、代わりに6ハッシュマップを使用することができます...しかし、1つは(一つだけが文字列であるたと思うとき)タイプが何であるかをアプリオリを知らない限り、その後1は一読してい

#[derive(Debug, Hash, PartialEq, Eq)] 
enum Value { 
    UniformInt(i64), 
    UniformFloat32(f32), 
    UniformFloat64(f64), 
    DistinctInt(Vec<i64>), 
    DistinctFloat32(Vec<f32>), 
    DistinctFloat64(Vec<f64>), 
} 

struct Particles { 
    store: HashMap<String, Value>, 
} 

一度に1つずつすべてのハッシュマップ:迷惑と時間の無駄。

+0

すぐにお返事ありがとうございます。属性の柔軟性が必要なため、DODを実装するこの複雑な方法に到達しました。通常、詳細な属性情報(すなわち、属性の数および正確な意味)は、アーティストからのものである。言い換えれば、この情報は動的であり、ランタイムの前に決定することはできません。 – TheBusyTypist

+0

@TheBusyTypist:そ​​うです...関連する詳細を含む質問を編集してください。また、 'f32'(' HashMap > 'はかなりうまくいくでしょう)をサポートする必要があるのか​​、複数のタイプをサポートする必要があるのか​​を知ることも価値があります。 –

+0

私は 'f64'や' int's(パーティクルIDのような)のような他のプリミティブ型をサポートしなければなりません。あなたが提案したように、別の 'HashMap'で属性をグループ化することは、その型によって有効な解決策になると思います。 – TheBusyTypist

関連する問題