30

質問:

スタックへのアクセスはメモリへのアクセスと同じ速度ですか?

たとえば、スタック内でいくつかの作業を行うことを選択したり、メモリ内のラベル付けされた場所で直接作業を行うことができます。

だから、具体的には:とpush ax同じ速度mov [bx], axですか?同様にpop ax、速度はmov ax, [bx]?(bxがnearメモリ内の場所を保持していると仮定します。)

質問の動機:

Cでは、パラメーターを受け取る自明な関数を思いとどまらせるのが一般的です。

これは、パラメーターがスタックにプッシュされ、関数が戻ったときにスタックからポップされる必要があるだけでなく、関数呼び出し自体がCPUのコンテキストを保持する必要があるため、つまりスタックの使用量が増えるためだといつも思っていました。

しかし、見出しの質問に対する答えを知っていると仮定すると、関数がそれ自体をセットアップするために使用するオーバーヘッド(プッシュ/ポップ/コンテキストの保持など)を、同等の数のダイレクトメモリアクセスの観点から定量化できるはずです。したがって、見出しの質問。


編集:明確化:上記で使用されているのは、16ビットx86アーキテクチャのセグメント化されたメモリモデルnearとは対照的です。)far

4

2 に答える 2

20

今日、あなたのCコンパイラはあなたを裏切ることができます。単純な関数をインライン化する場合があります。その場合、関数の呼び出しや戻りは行われず、おそらく、正式な関数パラメーターの受け渡しとアクセスに関連する追加のスタック操作(または関数がインライン化されているが、使用可能なレジスターは使い果たされます)すべてがレジスターで実行できる場合、またはさらに良いことに、結果が定数値であり、コンパイラーがそれを確認して利用できる場合。

関数呼び出し自体は、それらが繰り返され、個別の命令キャッシュとさまざまな予測メカニズムがあり、効率的なコード実行に役立つ場合、最新のCPUでは比較的安価です(ただし、必ずしもゼロコストではありません)。

それ以外は、「ローカル変数とグローバル変数」の選択によるパフォーマンスへの影響は、メモリ使用パターンに依存すると思います。CPUにメモリキャッシュがある場合、スタックに大きな配列や構造体の割り当てと割り当て解除を行ったり、深い関数呼び出しや深い再帰を行ってキャッシュミスを引き起こしたりしない限り、スタックはそのキャッシュにある可能性があります。対象のグローバル変数が頻繁にアクセスされる場合、またはそのネイバーが頻繁にアクセスされる場合、その変数もほとんどの場合キャッシュにあると思います。繰り返しになりますが、キャッシュに収まらない大量のメモリにアクセスしている場合は、キャッシュミスが発生し、パフォーマンスが低下する可能性があります(おそらく、キャッシュに適した、より優れた方法がある場合とない場合があります)。やってみたいです)。

ハードウェアがかなり馬鹿げている場合(キャッシュがないか小さい、予測がない、命令の並べ替えがない、投機的実行がない、何もない)、誰もが数えるので、明らかにメモリの負荷と関数呼び出しの数を減らしたいと思います。

さらに別の要因は、命令の長さとデコードです。(スタックポインタに対して)スタック上の場所にアクセスするための命令は、特定のアドレスの任意のメモリ位置にアクセスするための命令よりも短くなる可能性があります。短い命令はデコードされ、より速く実行される可能性があります。

パフォーマンスは以下に依存するため、すべてのケースに明確な答えはないと思います。

  • あなたのハードウェア
  • コンパイラ
  • プログラムとそのメモリアクセスパターン
于 2012-10-07T06:49:13.963 に答える
14

クロックサイクルに興味がある人のために...

特定のクロックサイクルを確認したい場合は、さまざまな最新のx86およびx86-64 CPUの命令/レイテンシテーブルをここで入手できます(これらを指摘してくれたhirschhornsalzに感謝します)。

次に、Pentium4チップで次のようになります。

  • push axおよびmov [bx], ax(赤いボックス)は、効率が実質的に同じであり、レイテンシとスループットは同じです。
  • pop axおよびmov ax, [bx](青いボックス)も同様に効率的でmov ax, [bx]、レイテンシが2倍であるにもかかわらず、スループットは同じです。pop ax

Pentium4命令タイミングテーブル

コメント(3番目のコメント)の後続の質問に関する限り:

  • 間接アドレス指定(つまりmov [bx], ax)は、直接アドレス指定(つまりmov [loc], ax)と実質的に違いはありません。ここで、locは、即値を保持する変数ですloc equ 0xfffd

結論:これをAlexeyの完全な答えと組み合わせると、スタックを使用し、関数をインライン化するタイミングをコンパイラーに決定させることの効率について、かなり堅実なケースがあります。

(補足:実際、1978年の8086までさかのぼると、これらの古い8086命令タイミングテーブルからわかるように、スタックの使用は、メモリへの対応するmovよりも効率が悪くありませんでした。)


レイテンシーとスループットを理解する

最新のCPUのタイミングテーブルを理解するには、もう少し必要な場合があります。これらは役立つはずです:

于 2012-10-10T12:11:38.860 に答える