3

HostBoostマルチインデックスコンテナに格納されているクラスのオブジェクトに基づくエージェントベースモデルを高速化するための戦略を探しています。私はSharkを使用して、時間の大部分が関数によって消費されていることを確認しましたcalcSI()

ここに画像の説明を入力してください

関数は、クラスの他のインスタンスの属性に依存する特定の確率をcalcSI()クラスのすべてのインスタンスについて計算する必要があります。(のインスタンスは約10,000〜50,000あり、これらの計算はホストごとに約25,600回実行されます。)HostHostHost

プロファイルを正しく解釈している場合、費やされる時間の大部分はに費やされcalcSI()ますHost::isInfectedZ(int)。これは、次のタイプのBoostunordered_multimap内の何かのインスタンスをカウントするだけですInfectionMap

struct Infection {
public:
  explicit Infection( double it, double rt ) : infT( it ), recT( rt ) {}
  double infT;
  double recT;
};
typedef boost::unordered_multimap< int, Infection > InfectionMap;

のすべてのメンバーはをHost含みInfectionMap carriage、特定のキーに関連付けられてHost::isInfectedZ(int)いる数を単純にカウントします。Infectionsint

int Host::isInfectedZ( int z ) const {
  return carriage.count( z );
}

  1. countBoostの順序付けされていないマルチマップの関数のコストに関する情報を見つけるのに苦労しています。Host各キーのインスタンスの数(つまり、各キーにInfections関連付けられている数)を追跡するために、個別の2次元配列に追加してオーバーヘッドを増やす必要がありますintか?

  2. 1つまたは2つの不要な複合キーインデックスを削除するなど、Boostマルチインデックスの大規模な構造的オーバーホールがより役立つかどうか疑問に思っています。マルチインデックスのバックグラウンドメンテナンスはプロファイラーに表示されないため、(おそらくばかげて)それが大きいのではないかと心配しています。マルチインデックスには8つのインデックスがあり、そのほとんどはordered_non_uniqueです。

  3. プロファイラーに表示されない可能性のある他の懸念事項はありますか、それともプロファイラーからの主要な結果が欠落していますか?

calcSI()残念ながら、の並列化とマルチスレッド化はオプションではありません。

更新:InfectionMap carriage 10ペアを超えることはめったになく、通常は5未満であることを知っておくと役立つ場合があります。


更新2:上記の#1で提案した戦略を試し、それぞれHostに配列int carriageSummary[ INIT_NUM_STYPES ]を指定しました。配列は、の可能な値でインデックス付けされていますz(ほとんどのシミュレーションでは、10未満の可能な値があります)。各エントリの値は、に加えられた変更を追跡しますcarriageHost::isInfectedZ( int z )関数は次 のようになります。

int Host::isInfectedZ( int z ) const {
  //return carriage.count( z );
  return carriageSummary[ z ];
}
そして、この関数に費やされる時間大幅に短縮されたようです。現時点では正確な比較はできません。 ここに画像の説明を入力してください 明らかに、配列の使用はややかさばりますが、範囲が狭い場合は問題ありませんz。他のコンテナ(つまり、unordered_mapではない)は、より広い範囲でより効率的でしょうか?

マルチインデックスの変更に関するフィードバックもお待ちしています。

4

1 に答える 1

1

#1で提案したように、Host ::carriageのunordered_multimapの横にキャリッジカウント配列を維持し、両方を「同期」させてください。Host :: isInfectedZは、(うまくいけば)より高速なキャリッジカウント配列を使用します。

int Host::isInfectedZ( int z ) const {
  return carriageCount[ z ];
}

isInfectedに渡すことができる整数の範囲が大きい場合は、キャリッジ数に連想配列を使用してください。

連想配列には、std::mapまたはboost::unorderedを使用できます。ルックアップの場合、前者は対数的な時間計算量を持ち、後者は一定の時間計算量を持ちます。ただし、この連想配列は通常非常に小さいため、std::mapの方が実際には高速である可能性があります。std :: mapは、スペースのオーバーヘッドも少なくて済みます。両方を試して、プロファイラーを実行して確認してください。私の賭けはstd::mapにあります。:-)

編集:

私のコメントに対するあなたの答えを見て、キャリッジ数に通常の固定サイズの配列を使用することをお勧めします。連想配列のことは忘れてください。

EDIT2:

あなたはスクラップしたいかもしれません

typedef boost::unordered_multimap< int, Infection > InfectionMap;

このような小さなインデックスを扱っているので、独自の手書きのInfectionMapクラスをロールアップします。

アップデート#2への対応:

あなたが改善したのを見てうれしいです。たとえば16個の整数の固定配列よりも「かさばらない」コンテナが見つかるとは思えません。STLおよびブーストコンテナはメモリをチャンクに割り当て、要素が少ない場合でも、最終的には固定サイズの配列と同じ大きさになります。

あなたはboost::arrayに興味があるかもしれません。これは、Cスタイルの固定配列の周りにSTLのようなインターフェースをラップします。これにより、固定サイズの配列をstd::vectorまたはstd::mapに「スワップアウト」するのが簡単になります。

于 2011-01-26T20:36:30.750 に答える