私は、利便性と一貫性のために (つまりlog(x)/log(2)
の代わりにlog(x)/0.3...
) 定数式の数学関数を使用する傾向があります。これらの関数は実際には言語自体の一部ではないため、定義されていないmath.h
(宣言されているだけ) ため、定数関数はコンパイル時に事前に計算されますか、それとも実行時に無駄に計算されますか?
6 に答える
コンパイラと最適化フラグに依存します。@AndrewyT が指摘しているように、GCC には属性を介して定数で純粋な関数を指定する機能があり、この場合、答えは肯定的であり、簡単に確認できるように結果をインライン化します。
$ cat constant_call_opt.c
#include <math.h>
float foo() {
return log(2);
}
$ gcc -c constant_call_opt.c -S
$ cat constant_call_opt.s
.file "constant_call_opt.c"
.text
.globl foo
.type foo, @function
foo:
pushl %ebp
movl %esp, %ebp
subl $4, %esp
movl $0x3f317218, %eax
movl %eax, -4(%ebp)
flds -4(%ebp)
leave
ret
.size foo, .-foo
.ident "GCC: (Ubuntu 4.3.3-5ubuntu4) 4.3.3"
.section .note.GNU-stack,"",@progbits
関数呼び出しはなく、定数をロードするだけです ( 0x3f317218 == 0.69314718246459961 == log(2)
)
現在、他に試すコンパイラはありませんが、簡単な最適化であるため、主要なすべての C コンパイラで同じ動作が期待できると思います。
一部のコンパイラはコンパイル時にそれらを評価しますが、この動作は保証されていません (そうすることで問題が発生する可能性もあります)。コンパイラをチェックして、その動作を確認する必要があります。
多くのシステムでlog(2)
は、 は のマクロから利用できることに注意M_LN2
してください<math.h>
。
ライブラリ関数の場合、一部のコンパイラはこれらの関数を組み込み関数として実装する場合があります。つまり、コンパイラは、コンパイル時に呼び出しを定数に置き換える関数について十分に認識しています。これを行うかどうかは、コンパイラに依存します。実際には、一部のコンパイラーは、関数呼び出しを含まない場合でも、コンパイル時に浮動小数点式を事前に計算することに消極的であることに気付きます。
一般に、コンパイラがこれらの関数をコンパイル時に計算できるほど十分に認識していないと仮定すると、通常はコンパイル時に計算されず、計算されません。実行時の顕著な副作用があるのではないでしょうか?
一部のコンパイラには、ユーザーが関数に関する追加情報をコンパイラに提供できるようにする非標準のコンパイラ依存の拡張機能がある場合があります。これにより、コンパイラは関数呼び出しをより適切に最適化し、特定の呼び出しをコンパイルに置き換えることができるかどうかを判断することさえできます。時間の事前計算。たとえば、GCC コンパイラは、 や などの関数属性(GCC 固有の拡張機能) をconst
サポートしていpure
ます。コンパイル時に引数がわかっている場合、const
理論的には、属性を使用した関数の呼び出しをコンパイル時の事前計算に置き換えることができます。GCCが実際にこれを実行できるかどうかはわかりませんが。
C++ (質問に C のタグが付けられていますが) 言語では、計画されている新しい機能はconstexpr
、同様の目的を果たし、説明した効果を持つはずの宣言指定子です。
それらは通常、実行時に計算されます (それらをインライン化する方法については他の回答を参照してください)。
単に定数値を入れるのではなく、値の意味を表す#define
またはconst
変数 ( PI
、LOG_2
など) を作成し、代わりにそれを使用します。
例えば:
#define LOG_2 0.30102999566
これは計算を行わず、任意の精度で値を指定するか、環境 (32 ビット対 64 ビット) で管理できるようにすることができます。
場合によります。コンパイラが実行時に行われるのとまったく同じように計算を実行でき、リンク時の最適化が実行され、ライブラリが何らかの中間形式で保持されている場合、はい、実行できます。
ほとんどのコンパイラは、そのすべてを行うわけではありません。
これは実行時に発生します。これは、関数のコードがリンク手順 (コンパイル手順の後に発生する) でのみ使用可能になるためです。
ステップを最適化するには、使用する前に初期化するグローバル変数を使用します。