23

たとえば、次のことを考慮してください。

int sum(int a, int b)
{
    return a + b;
}

対。

int sum(const int a, const int b)
{
    return a + b;
}

2番目のアプローチは一般的に速いですか?

Cの関数パラメーターはコピーされて関数に送信されるため、関数内の変更が元の値に影響を与えることはありません。私の推論は、sum上記の2番目の例では、コンパイラーは関数内で変更されていないことを確実に認識しているためab最初にコピーせずに元の値を渡すことができるということです。sumそのため、2番目の方が最初の方より速いと思います。しかし、私は本当に知りません。上記の特定の単純な例ではsum、違いがあったとしても最小限に抑える必要があります。

編集:sum例は私のポイントを説明するためだけのものです。この特定の例では、大きな違いがあるとは思いません。constしかし、もっと複雑な状況では、関数パラメーター内の修飾子をコンパイラーが利用して、関数を高速化できるのではないかと思います。コンパイラーが、パラメーターが関数内で変更されているかどうかを常に判断できるとは思えません(したがって、以下の2番目の質問)。constしたがって、修飾子が見つかった場合は、修飾子がない場合とは異なる動作をすることを期待しconstます。

質問:一般に、関数は、引数がそうでない場合よりも、引数がそうである場合の方が高速になりますconstか?

質問2:一般に、Cコンパイラは(理論的には)関数パラメータが関数内で変更されているかどうかを常に判断できますか?

4

4 に答える 4

17

簡単な答え:いいえ

長い答え、いいえ、証拠があります。

このテストを数回実行しましたが、clangでコンパイルしたMacBook Proで、リアルタイムの違いは見られませんでした。

int add(int a, int b)
{
    return a + b;
}

const int cadd(const int a, const int b)
{
    return a + b;
}

int main (int argc, char * argv[])
{
#define ITERS 1000000000

    clock_t start = clock();
    int j = 0;
    for (int i = 0; i < ITERS; i++)
    {
        j += add(i, i + 1);
    }

    printf("add took %li ticks\n", clock() - start);

    start = clock();
    j = 0;
    for (int i = 0; i < ITERS; i++)
    {
        j += cadd(i, i + 1);
    }

    printf("cadd took %li ticks\n", clock() - start);

    return 0;
}

出力

追加には4875711ティックかかりました
CADは4885519ティックを取りました

clockただし、これらの時間は、タイミング関数の中で最も正確ではなく、他の実行中のプログラムの影響を受ける可能性があるため、実際には一粒の塩で取る必要があります。

したがって、生成されたアセンブリの比較は次のとおりです。

_add:
    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset %rbp, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register %rbp
    movl    %edi, -4(%rbp)
    movl    %esi, -8(%rbp)
    movl    -4(%rbp), %esi
    addl    -8(%rbp), %esi
    movl    %esi, %eax
    popq    %rbp
    ret

_cadd:                                 
    .cfi_startproc    
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset %rbp, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register %rbp
    movl    %edi, -4(%rbp)
    movl    %esi, -8(%rbp)
    movl    -4(%rbp), %esi
    addl    -8(%rbp), %esi
    movl    %esi, %eax
    popq    %rb

ご覧のとおり、この2つに違いはありません。呼び出し元への単なるヒントとして引数を渡すと、引数constは変更されず、上記のような単純なシナリオでは、別のアセンブリがコンパイルされることはありません。

于 2012-09-06T01:15:37.533 に答える
9

答えはおそらく、コンパイラ、最適化レベル、およびコンパイラが関数をインライン化することを決定するかどうかによって異なります。これらのことに興味がある場合は、コンパイラによって生成された実際のアセンブリを見て、見つけるのは簡単です。

于 2012-09-06T00:54:43.267 に答える
0

いいえ、両方とも同じ速度である必要があります。あなたの理由で、元の値をsum関数に渡すと仮定します。たとえば、別のスレッドなど、sum関数からのコードが元の値を変更するのはどうでしょうか。

一般に、constは引数のパフォーマンスに影響を与えません。constがローカル/グローバル変数である場合、一部の計算はconstであるかのようにコンパイル時間に移動できるため、パフォーマンスに影響を与えます。

于 2012-09-06T00:59:27.680 に答える
0

パーティに遅れましたが、コンパイラはconstとして定義された変数を読み取り専用メモリセグメント/ブロックに配置できるため、ポインタの不正操作を介してアドレスに書き込もうとすると、メモリへの書き込みによってメモリへの書き込みがトリガーされます。実行時の例外。

-ジェイミー

于 2017-03-31T20:23:26.580 に答える