32

C 言語では、なぜ はn++よりも速く実行されるのn=n+1ですか?

(int n=...;  n++;)
(int n=...;  n=n+1;)

私たちのインストラクターは、今日のクラスでその質問をしました. (これは宿題ではありません)

4

10 に答える 10

102

あなたが「石器時代」のコンパイラで作業しているなら、それは本当でしょう...

「石器時代」の場合: 機械が通常持っているよりも速いよりも
++n速いn++n=n+1
increment xadd const to x

  • の場合、n++2つのメモリアクセスのみがあります(読み取りn、inc n、書き込みn)
  • の場合、n=n+13つのメモリアクセスがあります(nの読み取り、constの読み取り、nの追加とconst、nの書き込み)

しかし、今日のコンパイラは自動的にに変換n=n+1され++n、想像以上のことをします!!

また、今日の故障したプロセッサでは、「石器時代」のコンパイラの場合にもかかわらず、多くの場合、 ランタイムはまったく影響を受けない可能性があります

関連している

于 2010-05-21T18:59:38.603 に答える
42

X86用のGCC4.4.3では、最適化の有無にかかわらず、まったく同じアセンブリコードにコンパイルされるため、実行に同じ時間がかかります。アセンブリでわかるように、GCCは単純にに変換n++n=n+1、それを1つの命令の追加(-O2内)に最適化します。

より高速なインストラクターの提案n++は、のインプレース更新命令を選択するのに十分なほど賢くなかった、非常に古い、最適化されていないコンパイラーにのみ適用されますn = n + 1。これらのコンパイラは、PCの世界では何年も前から廃止されていますが、奇妙なプロプライエタリ組み込みプラットフォームではまだ見つかる可能性があります。

Cコード:

int n;

void nplusplus() {
    n++;
}

void nplusone() {
    n = n + 1;
}

出力アセンブリ(最適化なし):

    .file   "test.c"
    .comm   n,4,4
    .text
.globl nplusplus
    .type   nplusplus, @function
nplusplus:
    pushl   %ebp
    movl    %esp, %ebp
    movl    n, %eax
    addl    $1, %eax
    movl    %eax, n
    popl    %ebp
    ret
    .size   nplusplus, .-nplusplus
.globl nplusone
    .type   nplusone, @function
nplusone:
    pushl   %ebp
    movl    %esp, %ebp
    movl    n, %eax
    addl    $1, %eax
    movl    %eax, n
    popl    %ebp
    ret
    .size   nplusone, .-nplusone
    .ident  "GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3"
    .section    .note.GNU-stack,"",@progbits

出力アセンブリ(-O2最適化あり):

    .file   "test.c"
    .text
    .p2align 4,,15
.globl nplusplus
    .type   nplusplus, @function
nplusplus:
    pushl   %ebp
    movl    %esp, %ebp
    addl    $1, n
    popl    %ebp
    ret
    .size   nplusplus, .-nplusplus
    .p2align 4,,15
.globl nplusone
    .type   nplusone, @function
nplusone:
    pushl   %ebp
    movl    %esp, %ebp
    addl    $1, n
    popl    %ebp
    ret
    .size   nplusone, .-nplusone
    .comm   n,4,4
    .ident  "GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3"
    .section    .note.GNU-stack,"",@progbits
于 2010-05-21T19:25:38.353 に答える
14

コンパイラーはn + 1無に最適化します。

どういう意味n = n + 1ですか?

その場合、それらは同一のアセンブリにコンパイルされます。(最適化がオンになっていて、それらが式ではなくステートメントであると仮定します)

于 2010-05-21T18:58:59.037 に答える
5

誰が言うの?あなたのコンパイラはそれをすべて最適化します、本当に、それを論点にします。

于 2010-05-21T18:58:48.277 に答える
3

最新のコンパイラは、両方の形式を同等のものとして認識し、ターゲット プラットフォームで最適に機能する形式に変換できるはずです。このルールには例外が 1 つあります。それは、副作用のある変数アクセスです。たとえば、nがメモリ マップされたハードウェア レジスタである場合、そこからの読み取りと書き込みは、データ値を転送するだけではありません (たとえば、読み取りによって割り込みがクリアされる場合があります)。キーワードを使用して、volatileへのアクセスの最適化に注意する必要があることをコンパイラに知らせます。その場合、コンパイラは(インクリメント操作) と(読み取り、追加、および格納操作) とnは異なるコードを生成する可能性があります。ただし、通常の変数の場合、コンパイラは両方の形式を同じものに最適化する必要があります。n++n = n + 1

于 2010-05-21T19:55:48.747 に答える
2

実際には、その理由は、演算子が接頭辞の場合と接頭辞の場合で定義が異なるためです。 ++n「n」をインクリメントして「n」への参照を返し、「n」をインクリメントすると「n」のコピーをn++返します。constしたがって、フレーズn = n + 1はより効率的になります。しかし、私は上記のポスターに同意する必要があります。優れたコンパイラは、未使用のリターンオブジェクトを最適化する必要があります。

于 2010-05-21T19:32:41.373 に答える
2

C 言語では、n++式の副作用は定義上、式の副作用と同等n = n + 1です。コードは副作用のみに依存しているため、正しい答えは、これらの式が常に正確に同等のパフォーマンスを発揮することであることがすぐにわかります。(この問題は最適化とはまったく関係がないため、コンパイラの最適化設定に関係なく、ところで.)

これらの式のパフォーマンスの実質的な相違は、コンパイラが意図的に (そして悪意を持って!) その相違を導入しようとしている場合にのみ可能です。しかし、この場合、もちろん、どちらの方向にも進む可能性があります。つまり、コンパイラの作成者がそれをゆがめたいと思った場合は、どちらの方向にも進むことができます。

于 2010-05-21T19:55:46.127 に答える
2

そうではありません。コンパイラは、ターゲット アーキテクチャに固有の変更を行います。このようなマイクロ最適化には疑わしい利点があることがよくありますが、重要なのは、プログラマーの時間に見合う価値がないことです。

于 2010-05-21T19:09:48.590 に答える
1

ソフトウェアというよりはハードウェアの問題だと思います...私の記憶が正しければ、古いCPUでは、n=n+1には2つのメモリ位置が必要で、++nは単にマイクロコントローラのコマンドです...しかし、私は疑問に思っていますこれは現代のアーキテクチャに適用されます...

于 2010-12-24T00:09:56.243 に答える
0

これらはすべて、コンパイラ/プロセッサ/コンパイル ディレクティブに依存します。したがって、「一般的に何が速いか」という仮定はお勧めできません。

于 2010-12-23T21:52:45.657 に答える