関数のインライン化基準
C++/C99言語では、"inline"キーワードが正式にサポートされており、コンパイラに対して関数を「インライン化してほしい」と要求することができます。しかし、良く知られていることですがこれはあくまでヒントなので、コンパイラの気分次第でインライン化が起きたり起きなかったり曖昧な点があります。
そこで、g++で、ある関数(フリー関数およびメンバ関数)がどういう時にインライン展開されるのかをまとめてみました。g++ 3.3.2 で -S したアセンブリ言語のリストを目視で確認しました。なお、「ある関数」の処理内容は "整数値123をリターンするだけ" と、充分に単純なものしておきました。
結果、おおよそ次のような基準でインライン展開の有無を決定していると思われます。
ただしワタクシ、横着してGCCのソースは一切読んでいませんのでその点ご注意ください(笑)。
if (関数に__attribute__((always_inline)) が指定されている?) { インライン化する; } else if (最適化(-O1以上)あるいは-finlineが指定されている?) { if (関数がinline修飾されている?) { if (充分に単純な関数?) { // (1) インライン化する; } else { インライン化しない; } } else if (-finline-functionsが指定されている?) { if (充分に単純な関数?) { // (2) インライン化する; } else { インライン化しない; } } else { インライン化しない; } } else { インライン化しない; } ※ (1)と(2)は微妙に判断基準が異なるようだ。(1)のほうがtrueになりやすいように思う。 ※ -O3以上では-finline-functionsが自動設定される。
なお、「inline修飾」については、C++のヘッダファイル中に直接
class Foo { public: int foo(void) { return 123; } };
のようにして実装を記述した場合も「inline修飾」ありと同等の結果になりました。ただし、
class Foo { public: int foo(void); }; inline int Foo::foo(void) { return 123; }
のようにしてヘッダファイル内だがクラス定義部分の外に実装を記述した場合には、明示的にinlineと書かなければ「inline修飾」なしと同等の結果になりました。
最後に、inline修飾が効かなかった場合を検出するため、時間性能を気にしなければならない場所では-Winline(下記のヘルプ参照)を使いましょう。
$ LANG=C g++ -v --help 2>&1 | grep inline | sort --param max-inline-insns=<value> The maximum number of instructions in a function that is eligible for inlining -Wextern-inline Warn when a function is declared extern, then inline -Winline Warn when an inlined function cannot be inlined -finline Pay attention to the 'inline' keyword -finline-functions Integrate simple functions into their callers -finline-limit=<number> Limits the size of inlined functions to <number> -fkeep-inline-functions Generate code for funcs even if they are fully inlined -fno-default-inline Do not inline member functions by default -fno-implement-inlines Export functions even if they can be inlined -fno-implicit-inline-te Only emit explicit instatiations of inline templates -minline-all-stringops Inline all known string operations -mno-inline-all-stringops Do not inline all known string operations
余談ですが、gcc --help すると、infoにも載っていないようなオプションが出てきたりしてびっくりです(w
(続き) ctor/dtorに関する注意
コンストラクタ、デストラクタをコンパイラに自動生成させる場合、当然かもしれませんがそれらはインライン化されます。インライン化というか何も処理が発生しないというか。
しかしもちろん、
[foo.h] class Foo { public: Foo(); ~Foo(); }; [foo.cpp] Foo::Foo(){} Foo::~Foo(){}
こう書いてしまうと構築、解体処理が.cppに記述した関数へのcall命令に変換されますから、非効率です。気をつけましょう。何も処理をしないなら、
[foo.h] class Foo { public: Foo(){} ~Foo(){} };
で良いでしょう。
(続き) 仮想関数のインライン化
散々言われている事とは思うけども、仮想関数もインライン展開の対象に なる ので要注意です。
値オブジェクトのメソッドが呼ばれている場合は実行時に動的束縛する必要がないからインライン化できます。Foo::bar() が仮想関数として、
Foo f; f.bar();
はインライン展開できます。
おまけ:某所のログ
16:26 >k< -finline : inlineキーワードを見る/見ない 16:26 >k< -finline-functions : inlineキーワードの有無に関係なく簡単な関数はインライン展開する 16:26 >k< のようですね 16:26 >k< でも、-finline(か最適化)していない場合は-finline-functionsは無視されるようだ 16:26 >k< いみわかんね>gcc