コンパイラの気持ちを汲んであげよう 第二回
前回の話は僕の中で好評だったので、続き。
で、前回勢いで「基本その一」とか書いたわりに、あのあと思い付かなかったので、その一で終わりなのであった。なんだけど、無理矢理その二とその三を書いておく
- その二。関数呼び出しは重い
- その三。ループとかは思っているよりも良いコードを出す
三については明日書こう。
今日は「その二、関数呼び出しは重い」について、なんだけど、ほとんど前回の話と同じ話なので注意。前回の話は、ひとことで書くと、
「関数に引数で渡されたポインタや、他の関数に渡すポインタは指す先がどうなってるかわからないから最適化が難しい」
と、いう話だった。と、いうことは、つまり、関数呼び出しがあれば、それだけ最適化が邪魔される、ということだ。
さらにいうと、関数呼び出しによって邪魔される最適化はポインタまわりだけではない。関数の先ではレジスタがどう使われるかわからないので、状況によっては必要も無いのにレジスタを保存しないといけなくなるようなこともあるだろうし、レジスタがもう一個余ってたらできただろう最適化も関数呼び出しがあるために、不可能になってしまう場合もあるかもしれない。
で、それだけの話。思ったより短くなってしまった…バランスが悪い。とりあえず、そういうふうに考えると、インライン展開の効果はそれなりにある、と思ってよいかもしれない。インライン展開はcall-returnが無くなるだけでなくて、「関数の中身をコンパイラに教えてやる」という効果もある、と考えておいていいだろう。「call-returnは軽いかもしれないが、関数呼び出しは重い」、ということは頭の片隅にでも。
あと、GCCでは、同じファイル内での関数呼び出しの最適化をしてくれる(VC6ではしてくれなかったよ、とか書いておく)。前に紹介した、combineオプションも試してみるべきかもしれない、けど、今回の主旨は「ささやかな努力でささやかに最適化しよう」と、いうことなので、わざわざcombineを使うのもどうかと思う。
さらにどうでもいい補足として、関数を越える最適化について、僕の知ってる限りで一番優秀なのは、ocamloptだったりする。ファイルを超えてインライン展開するコンパイラは他に知らない。ocamloptは、なんか、色々素晴らしすぎると思う。