たとえば、次のことを考慮してください。
int sum(int a, int b)
{
return a + b;
}
vs.
int sum(const int a, const int b)
{
return a + b;
}
一般的に、2番目のアプローチの方が高速ですか?
Cの関数パラメーターがコピーされて関数に送信されるため、関数内での変更が元の値に影響を与えません。上記の2番目のsum
では、コンパイラはa
とb
が関数内で変更されないことを確実に知っているため、最初に値をコピーせずに元の値を渡すことができます。そのため、2番目のsum
は最初のものよりも速いと思います。しかし、私は本当に知りません。上記のsum
の特定の単純な例では、違いがあっても最小限に抑える必要があります。
編集:sum
の例は、私のポイントを説明するためのものです。この特定の例では、大きな違いがあるとは思いません。しかし、より複雑な状況では、関数パラメーター内のconst
修飾子をコンパイラーが利用して、関数を高速化できるかどうか疑問に思います。コンパイラーがパラメーターが関数内で変更されたかどうかを常に判別できるとは思えません(したがって、以下の2番目の質問)。したがって、const
修飾子が見つかった場合、const
修飾子がない場合とは異なる動作をすることを期待します。
質問:一般に、引数がconst
である場合、そうでない場合よりも関数の方が高速になりますか?
質問2:一般に、Cコンパイラは(理論的には)関数のパラメーターが関数内で変更されたかどうかを常に判断できますか?
短い答え:いいえ
長い答え、いいえ、証明あり
私はこのテストを数回実行しましたが、clangでコンパイルしたMacBookプロでリアルタイムの違いは見られませんでした。
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;
}
出力
addは4875711ティックを取得しました caddは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つの間にNo差異があります。引数をconst
として渡すことは呼び出し側へのヒントにすぎず、引数は変更されません。また、上記のような単純なシナリオでは、異なるアセンブリがコンパイルされることはありません。
答えは、コンパイラ、最適化レベル、およびコンパイラが関数をインライン展開するかどうかによって異なります。これらのことに興味がある場合は、コンパイラによって生成された実際のアセンブリを見て、それを見つけるのは簡単です。
パーティーに遅れましたが、コンパイラはconstとして定義された変数を読み取り専用のメモリセグメント/ブロックに配置できるため、アドレスへの書き込みが試みられた場合、ポインタへのポインタを介して、メモリへの書き込みがトリガーされます。実行時の例外。
-ジェイミー
いいえ、どちらも同じ速度でなければなりません。あなたの理由で、元の値をsum関数に渡すと仮定します。sum関数からの一部のコードは、たとえば別のスレッドなど、元の値を変更します。
一般に、constは引数のパフォーマンスに影響を与えません。 constがローカル変数またはグローバル変数の場合、一部の計算をconstであるかのようにコンパイル時間に移動できるため、パフォーマンスに影響します。