1

簡単に言えば、次のうちどれがより良い選択になる可能性があります。

somestruct data[lots];
for(int i=0;i<lots;i++) {
    a(&data[i]);
    b(&data[i]);
    c(&data[i]);
}
//....
void a(somestruct* d) { ..stuff with d.. }
void b(somestruct* d) { ..stuff with d.. }
void c(somestruct* d) { ..stuff with d.. }

また

somestruct data[lots];
a(lots,data);
b(lots,data);
c(lots,data);
//...
void a(int n, somestruct* d) {
    for(int i<n;i++) {
        ..stuff with d[i]..
} }
void b(int n, somestruct* d) {
    for(int i<n;i++) {
        ..stuff with d[i]..
} }
void c(int n, somestruct* d) {
    for(int i<n;i++) {
        ..stuff with d[i]..
} }

私の理解では、A の場合、現在アクティブな構造がキャッシュされて改善されますが、関数呼び出しが大量に発生します (マイナス)。一方、B の場合、キャッシュを破棄しますが、3*lots 関数呼び出しではなく、3 つの関数呼び出しがあります。

私のコンパイラが a、b、および c をインライン化することを決定した場合、最初のオプションが最良の選択であるはずです (現在、両方の長所が得られているため)。関数呼び出しは、メモリ アクセスよりもはるかに高いです。

知る唯一の方法は、特定のアプリケーションをベンチマークすることであることは承知していますが、ここで見逃していた経験則があるかどうか知りたいと思っていました。最初のバージョンは、私の特定のケースに対してややクリーンなコードを生成しますが、違いはほとんどありません。

編集

関数に入るコスト (スタック ポインターの割り当てなど) とキャッシュ ミスのコストを比較する方法について質問しようとしていました。一般的な回答は、他の人にも役立つと思います。ただし、質問の一般的な形式には満足のいく回答ができないようです。そのため、正しく数えたと仮定して、完全な統計を以下に示します。

  • data は 100,000 個の構造体で、それぞれに 6 つの double と int が含まれています。
  • a() は、6 つの double 読み取り、3 つの double 乗算、3 つの double add、および 3 つの double store です。
  • b() は、3 回の二重読み取り、3 回の二重乗算、4 回の加算、2 回の二重比較、および条件付きの c の呼び出しです。
  • c() は、3 つの二重乗算、3 つの関数呼び出し、および 3 つの二重書き込みです。
4

1 に答える 1

0

尋ねるべき主な質問は、関数呼び出しとメモリ アクセス (特にキャッシュ ミス) の違いは何かということです。データ構造が大きい (キャッシュが保持できないほど大きい) 場合、いくつかのキャッシュ ミスが発生します。あなたが提供した最初のケ​​ースでは、これらのキャッシュ ミスは 3 回繰り返されますが、最初のケースでは 1 回だけ発生します。さらに、関数は、10 要素ごとのような順序でデータ構造にアクセスする場合があります。これにより、キャッシュ ミスの数が増加します。データ構造が十分に小さい場合、キャッシュ ミスは発生せず、関数呼び出しのオーバーヘッドが引き継ぎます (コンパイラがそれらをインライン化しない場合)。しかし、一般に、分析されるデータ構造のサイズを推測することはできないため、問題を一般的なものとして、最初の解決策を選択します。

于 2013-10-19T20:46:45.797 に答える