目次: Linux
たまにLinux kernelをコンパイルしていると 'func' is deprecated. と言われることがあります。自分が見かけたのは、kernel/intermodule.cというファイルのinter_module_registerという関数でした。(昔の2.6系には残っていましたが、最新版(2.6.23.8)では存在しません)
見てみると関数の宣言に __deprecatedと書いてあり、define部分を探すとinclude/linux/compiler.hに書いてあります。しかし宣言の中身は空、というのもgccだけがdeprecated警告をするので、include/linux/compiler-gcc.hの方に書いてあるんですね。
#define __deprecated __attribute__((deprecated))
こんな定義です。関数プロトタイプに対して __attribute__((deprecated)) と書いておくと、その関数を使った人に対して冒頭で述べたような警告がでます。
これはコードの作者あるいは古いコードをいつまでも使っている人への警告でしょうね。もう使われない(deprecated)関数をお使いのようですけど、いずれなくなってあなたのコードは動かなくなりますよ!それまでには直してくれよ、ヨロシクね!ってことです。
他にcompiler.hで面白いなと思うのは、likelyとunlikelyマクロでしょうか。以下のようなマクロです。
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
キーワード __builtin_expectはコンパイラに対して、第一引数の結果が第二引数の結果になるときに高速動作するコードを出力しなさい、とコンパイラにヒントを与えます。ヒントが正しければプログラムが高速に動作するはずです。
具体的には likelyマクロは引数xが真であるとき、unlikelyは逆にxが偽であるときに高速動作せよと、コンパイラに指示しています。ではlikelyとunlikelyの効果を以下の実験コードで確かめてみましょう。
int func_likely();
int func_unlikely();
volatile int i;
int main(int argc, char *argv[])
{
func_likely();
func_unlikely();
return 0;
}
int func_likely()
{
int c = 0;
while (likely(i)) {
c++;
}
return c;
}
int func_unlikely()
{
int c = 0;
while (unlikely(i)) {
c++;
}
return c;
}
注目点はfunc_likelyとfunc_unlikelyです。ループの条件に付けたlikelyマクロとunlikelyマクロ以外に違いがないことを確認していただけると思います。これをgccでコンパイルしてみましょう。2.95でも対応していたと思いますが、3.xくらいのgccなら確実だと思います。
あとコンパイル時に -O2を付けることを忘れずに。
$ emacs a.c $ gcc --version gcc (GCC) 4.1.2 20061115 (prerelease) (Debian 4.1.1-21) $ gcc -O2 -S a.c $ cat a.s (コードは一部抜粋です) (func_likelyの説明) カウント文と分岐文が連続している。条件は真の可能性が高いと信じて いるから、「カウント分」「条件判定」「分岐」という一連の命令を連 続して並べる。これにはキャッシュに乗りやすい、プリフェッチしやす いなど様々な理由があると思う。 一方、リターン文は遠くに配置される。これはコンパイラにとって、ルー プの条件が成立しないのは、稀なケースであって滅多に実行しないと信 じているから。 func_likely: jmp .L8 ;(条件判定へ) .p2align 4,,7 .L9: incl %edx ;(カウント文) .L8: movl i, %eax testl %eax, %eax ;(i != 0の条件チェック) jne .L9 ;(分岐文、真だったらループの先頭へ) popl %ebp movl %edx, %eax ret ;(リターン文) (func_unlikelyの説明) カウント文とリターン文が連続している。 分岐文もあるが真じゃない可能性が高いと信じているので、条件は成立 せずに即座に終了するはずである。 逆に、条件が成立するときは遠くに置いた判定文へ飛ぶ。これはコンパ イラにとって、条件が成立することが稀なケースであって、滅多に実行 しないと信じているから。 func_unlikely: .L2: movl i, %eax testl %eax, %eax ;(i != 0の条件チェック) jne .L3 ;(分岐文、真だったらカウント文へ) popl %ebp movl %edx, %eax ret ;(リターン文) .L3: incl %edx ;(カウント文) jmp .L2 ;(条件判定へ)
ヒントがどういう効果をおよぼすか、ご理解いただけたかと思います。このくらいの小さい関数では効率が変わらないので、例としてはあまり良くないかも…。
最後に !!(x) と引数を二重否定している理由ですが、1ではないけど真(5とか10とか -1とか)である値を1に正規化するためです。以下の例を見ていただくとわかるかと思います。
x = 5 //真だけど1ではない !(x) => 0 //偽 !!(x) => 1 //真で1である
お前の説明じゃまったくわからんぞ!!って人はGoogle先生にも聞きましょう。有名なマクロなので、他のサイトでも紹介されています。
< | 2007 | > | ||||
<< | < | 12 | > | >> | ||
日 | 月 | 火 | 水 | 木 | 金 | 土 |
- | - | - | - | - | - | 1 |
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 | 31 | - | - | - | - | - |
合計:
本日: