4

ベクトル/行列ライブラリを作成しています。(GCC、ARM NEON、iPhone)

typedef struct{ float v[4]; } Vector;
typedef struct{ Vector v[4]; } Matrix;

関数呼び出し時のデータコピーによるパフォーマンスの低下を避けるために、構造体データをポインターとして渡しました。だから私は最初にこのような関数を設計しました:

void makeTranslation(const Vector* factor, Matrix* restrict result);

しかし、関数がインラインの場合、パフォーマンスのために値をポインターとして渡す理由はありますか? それらの変数もコピーされますか? レジスタとキャッシュはどうですか?私はこのように機能を再設計しようとしました:

inline Matrix makeTranslation(const Vector factor) __attribute__ ((always_inline));

それぞれの場合の通話料金はどのようにお考えですか。

  • 提案を反映するために、2 番目の署名に「const」を追加しました。
4

1 に答える 1

4

関数がインラインの場合、通常、変数のコピーは呼び出しに直接関与しません。変数は、関数呼び出しの直接の結果としてではなく、通常の実行の一部として移動され、スタックに置かれることがあります。(レジスタが不足すると、一部の値がスタックなどに置かれる場合がありますが、必要な場合のみです。) したがって、関数がインライン化されると、「呼び出し」のオーバーヘッドは基本的に消えます (これ以上セットアップ/破棄する必要はありません)。スタック フレーム、無条件のジャンプ、パラメーターのプッシュ/ポップはもうありません。)

関数を常にalways_inlineインライン化する属性に依存できる場合は、Vector をポインターで渡すべきではありません (変更されていない場合)。この理由は、ポインタで渡すにはベクトルのアドレスを取得する必要があるためです。つまり、コンパイラはアドレスを持っていることを確認する必要があり、CPU レジスタだけに存在することはできません。これにより、不要な場合は速度が低下する可能性があり、何かのアドレスを取得すると、コンパイラーはアドレスが不要であることを確認できないため、常にアドレスがあることを確認します。

ポインターによる受け渡しのため、このコードには常に、オブジェクトのアドレスを取得するための命令と、メンバーの値を取得するための少なくとも 1 つの逆参照があります。値渡しの場合、これはまだ発生する可能性がありますが、コンパイラはそのすべてを最適化できる場合があります。

インライン展開を使いすぎると、コンパイラのバイナリ コードのサイズが大幅に増加する可能性があることを忘れないでください。特定のケースでは、(インライン関数の結果として) 大きなコード セグメントがあると、より多くの命令キャッシュ ミスが発生し、パフォーマンスが低下する可能性があります。これは、CPU がプログラムの一部をフェッチするために常にメイン メモリにアクセスする必要があるためです。大きすぎて小さな L1 キャッシュに収まりません。これは組み込みプロセッサ (iPhone など) では特に重要です。これらのプロセッサは通常小さなキャッシュを持っているからです。

于 2010-05-01T14:03:56.287 に答える