C ++のインライン関数の利点?


254

C ++でインライン関数を使用する利点と欠点は何ですか?コンパイラーが出力するコードのパフォーマンスを向上させるだけであることがわかりますが、今日の最適化されたコンパイラー、高速CPU、巨大なメモリーなど(メモリーが不足していて、すべてが100KBのメモリーに収まらなければならなかった1980年とは異なります)彼らが今日持っている利点は何ですか?


48
これは、常識が間違っている質問の1つです。誰もが標準のComp Sci回答で答えました。(インライン化により、関数呼び出しのコストは節約されますが、コードサイズが増加します)。ごみ。コンパイラーがより多くの最適化を適用するための単純なメカニズムを提供します。
マーティンヨーク

37
これは、コメントを装った回答の1つです。投稿された回答が気に入らない場合は、自分の回答を投稿して、それがどうなるか見てください。
Dave Van den Eynde、2010年

10
この質問の根拠には欠陥があります。C ++インライン関数は、コンパイル中にインライン化するコンパイラーとはほとんど関係がありません。残念なことに、それはinlinec ++キーワードであり、そのインライン化はコンパイラー最適化手法です。正しい答えについては、この質問「関数/メソッドのキーワードinlineをいつ書くべきか」を参照してください。
deft_code

3
@JoseVegaあなたのリンクは壊されました

回答:


143

インライン関数は、パラメーターや戻りアドレスなどのスタックのオン/オフをプッシュおよびポップする必要がないため、より高速です。ただし、バイナリが少し大きくなります。

大きな違いはありますか?ほとんどの場合、最新のハードウェアでは顕著に十分ではありません。しかし、それは違いを生む可能性があり、それは一部の人々にとっては十分です。

インラインで何かをマークしても、インラインになる保証はありません。これはコンパイラへの提案にすぎません。場合によっては、仮想関数がある場合や、再帰が関係している場合などは不可能です。また、コンパイラーは使用しないことを選択する場合もあります。

このような状況が検出可能な違いを生むのを見ることができました。

inline int aplusb_pow2(int a, int b) {
  return (a + b)*(a + b) ;
}

for(int a = 0; a < 900000; ++a)
    for(int b = 0; b < 900000; ++b)
        aplusb_pow2(a, b);

26
私が疑っていたように、インライン化は上記に違いはありません。gcc 4.01でコンパイルされています。バージョン1はインライン化の使用を強制されました:48.318u 1.042s 5:51.39 99.4%0 + 0k 0 + 0io 0pf + 0wバージョン2はインライン化を強制されません348.311u 1.019s 5:52.31 99.1%0 + 0k 0 + 0io 0pf + 0wこれは良い例は、常識が間違っていることでした。
マーティンヨーク

36
呼び出し自体は実際に重要ですが、それはインラインを使用することによって得られる小さな利益です。主な利点は、コンパイラーが、ポインターが相互にエイリアスしない場所、呼び出し元の変数が呼び出し先で終わる場所などを確認できるようになったことです。したがって、次の最適化がさらに重要です。
Johannes Schaub-litb 08年

31
関数の結果が使用されることも、関数が副作用を起こすこともないため、このスニペットではおそらく違いはありません。画像処理のインライン化で、測定可能なパフォーマンスの向上が見られます。
2010年

4
違いがない理由は、コンパイラが独自にインライン化する可能性があるためです。または、コードが小さいため、コードのプリフェッチの問題はありません。
einpoklum 2015

3
@einpoklumそのため、コンパイラはループ全体を最適化している場合さえあります。
noɥʇʎԀʎzɐɹƆ

197

メリット

  • 必要な場所にコードをインライン化することで、プログラムは関数呼び出しと戻り部分に費やす時間を短縮できます。コードが大きくなったとしても、コードを高速化するはずです(以下を参照)。ささいなアクセサのインライン化は、効果的なインライン化の例です。
  • インラインとしてマークすることで、関数定義をヘッダーファイルに入れることができます(つまり、リンカに文句を言わずに、複数のコンパイルユニットに含めることができます)。

短所

  • コードが大きくなる可能性があります(つまり、重要な関数にインラインを使用する場合)。そのため、ページングを引き起こし、コンパイラーによる最適化を無効にする可能性があります。
  • オブジェクト処理の内部を公開するため、カプセル化がわずかに解除されます(ただし、すべての「プライベート」メンバーも公開されます)。これは、PImplパターンでインライン化を使用してはならないことを意味します。
  • それはあなたのカプセル化をわずかに壊します2:C ++インライン化はコンパイル時に解決されます。つまり、インライン関数のコードを変更した場合、それを使用してすべてのコードを再コンパイルして、コードが確実に更新されるようにする必要があります(同じ理由で、関数パラメーターのデフォルト値は使用しません)。
  • ヘッダーで使用すると、ヘッダーファイルが大きくなるため、ユーザーが気にしないコードで興味深い情報(クラスメソッドのリストなど)が希釈されます(これが、インライン関数を内部で宣言する理由です)クラスですが、ヘッダーをクラス本体の後に定義し、クラス本体の内部には定義しません)。

インラインマジック

  • コンパイラーは、インラインとしてマークした関数をインライン化する場合としない場合があります。また、コンパイル時またはリンク時にインラインとしてマークされていない関数をインライン化することもできます。
  • インラインは、コンパイラーによって制御されるコピー/貼り付けのように機能します。これは、プリプロセッサーマクロとはかなり異なります。マクロは強制的にインライン化され、すべての名前空間とコードを汚染し、簡単にデバッグできず、さらに実行されますコンパイラが非効率的と判断した場合。
  • クラス自体の本体内で定義されたクラスのすべてのメソッドは、「インライン化された」と見なされます(コンパイラーがインライン化しないことを決定できる場合でも)
  • 仮想メソッドは傾斜可能であるべきではありません。それでも、コンパイラーがオブジェクトのタイプを確実に認識できる場合(つまり、オブジェクトが宣言され、同じ関数本体内で構築された場合)、コンパイラーはオブジェクトのタイプを正確に認識するため、仮想関数もインライン化されます。
  • テンプレートのメソッド/関数は常にインライン化されているわけではありません(ヘッダー内に存在しても、自動的にインライン化されません)。
  • 「インライン」の後の次のステップは、テンプレートのメタプログラミングです。つまり、コンパイル時にコードを「インライン化」することで、コンパイラが関数の最終結果を推定できる場合があります...したがって、複雑なアルゴリズムは一種のreturn 42 ;ステートメントに還元される場合があります。これは私にとって極端なインライン化です。実際にはめったに発生せず、コンパイル時間が長くなり、コードが肥大化せず、コードが高速になります。しかし、杯のように、ほとんどの処理はこの方法で解決できないため、どこにでも適用しようとしないでください...それでも、これはとにかくクールです...
    :-p

あなたはそれがあなたのカプセル化をわずかに壊すと言いました。例を挙げて説明してもらえますか?
デストラクタ

6
@PravasiMeet:C ++です。DLL /共有ライブラリをクライアントに配信し、クライアントがそれをコンパイルするとします。メンバー変数Xを使用して作業Yを実行するインライン関数fooは、クライアントのコードにインライン化されます。メンバー変数をZに変更したDLLの更新バージョンを提供し、作業Yに加えて作業YYを追加する必要があるとしましょう。クライアントは、DLLをプロジェクトとBOOMにコピーするだけです。fooのコードは彼らのバイナリにはあなたが書いた更新されたコードではありません...クライアントがあなたのプライベートコードに合法的にアクセスできないにもかかわらず、インライン化はそれをかなり「公開」します。
paercebal

@paercebal最後から2番目の箇条書きについて、関数テンプレートがインラインでない場合の例を挙げられますか?私はそれらが常にインラインであると思いました、しかし私は現在便利なリファレンスを持っていません(しかし、簡単なテストはそれを確認するようです)。
Konrad Rudolph

@KonradRudolph n4594で私は見る:3.2/6: There can be more than one definition of [..] inline function with external linkage [..] non-static function template。5.1.5 / 6でFor a generic lambda, the closure type has a public inline function call operator member template。また7.1.2/2: the use of inline keyword is to declare an inline function、呼び出しの時点で関数本体をインライン化することが提案されています。したがって、それらが同じように動作する場合でも、インライン関数と関数テンプレートは、混合可能な別々の直交概念(つまり、インライン関数テンプレート)であると
結論付け

カプセル化が壊れていますか?どうして?カプセル化は、プログラミングのためのものであり、メモリ内の実際のオブジェクトのためのものではありません。その時点では誰も気にしません。ライブラリを配布する場合でも、コンパイラーは独自にインライン化するかしないかを選択する場合があります。したがって、最終的に新しいライブラリを入手した場合は、そのライブラリの関数とオブジェクトを使用するすべてのものを再コンパイルする必要があります。
FalcoGer

42

古風なCおよびC ++では、コンパイラーに可能な最適化に関する提案(提案にすぎない)のinlineようなものregisterです。

最新のC ++では、inline複数の定義(宣言ではない)が異なる変換単位で見つかった場合、それらはすべて同じであり、リンカーは1つを自由に保持し、他のすべてを破棄できることをリンカーに伝えます。

inline ヘッダーファイルで関数が定義されている場合(複雑または「線形」に関係なく)、リンカによって「複数の定義」エラーが発生することなく複数のソースにその関数を含めることができるようにする必要があります。

(グローバル関数とは対照的に)テンプレート関数と同様に、クラス内で定義されたメンバー関数はデフォルトで「インライン」です。

//fileA.h
inline void afunc()
{ std::cout << "this is afunc" << std::endl; }

//file1.cpp
#include "fileA.h"
void acall()
{ afunc(); }

//main.cpp
#include "fileA.h"
void acall();

int main()
{ 
   afunc(); 
   acall();
}

//output
this is afunc
this is afunc

fileA.hが2つの.cppファイルに含まれているため、のインスタンスが2つあることに注意してくださいafunc()。リンカはそれらの1つを破棄します。no inlineが指定されている場合、リンカは文句を言うでしょう。


16

インライン化は、コンパイラが自由に無視できる提案です。コードの小さなビットに最適です。

関数がインライン化されている場合、基本的には、実際に別の関数を呼び出すのではなく、関数呼び出しが行われるコードに挿入されます。実際の通話を行う必要がないため、これにより速度が向上します。

また、呼び出しによって発生した新しい命令でパイプラインをリロードする必要がないため、CPUのパイプライン処理も支援します。

唯一の欠点は、バイナリサイズが大きくなる可能性があることですが、関数が小さい限り、これはあまり問題になりません。

私は最近、この種の決定をコンパイラーに任せる傾向があります(まあ、とにかく賢いものです)。それらを作成した人々は、基礎となるアーキテクチャについてはるかに詳細な知識を持つ傾向があります。


12

インライン関数は、コンパイラーが使用する最適化手法です。関数プロトタイプにインラインキーワードを付加するだけで、関数をインラインにすることができます。インライン関数は、その関数がコードで使用された場所に関数の完全な本体を挿入するようコンパイラーに指示します。

利点:-

  1. 関数呼び出しのオーバーヘッドは必要ありません。

  2. また、関数呼び出し中のスタックでの変数のプッシュ/ポップのオーバーヘッドを節約します。

  3. また、関数からの戻り呼び出しのオーバーヘッドを節約します。

  4. 命令キャッシュを利用して参照の局所性を高めます。

  5. インライン化後、コンパイラーは、指定されている場合はプロシージャー内最適化も適用できます。これは最も重要なものです。このようにして、コンパイラーはデッドコードの除去に集中できるようになり、分岐予測、誘導変数の除去などにより多くのストレスを与えることができます。

詳細については、このリンクhttp://tajendrasengar.blogspot.com/2010/03/what-is-inline-function-in-cc.htmlを参照してください。


4
1)これは提案であり、命令ではありません。2)一般的に使用される関数が大量にインライン化されている場合、コードサイズの増加により、キャッシュミスが増える可能性があります
Flexo

3
末尾のリンクはあなたの個人ブログですか?そうである場合は、そのように宣言する必要があります。そうでない場合は、スパムのように見えます。
Flexo

6

共有ライブラリを構築する際には、インライン関数が重要であることを付け加えたいと思います。関数をインラインでマークせずに、バイナリ形式でライブラリにエクスポートされます。エクスポートした場合、シンボルテーブルにも表示されます。一方、インライン化された関数は、ライブラリバイナリにもシンボルテーブルにもエクスポートされません。

ライブラリが実行時にロードされることを意図しているとき、それは重要かもしれません。また、バイナリ互換性のあるライブラリにヒットする可能性もあります。そのような場合はインラインを使用しないでください。


@Johnsyweb:私の答えを注意深く読んでください。実行可能ファイルをビルドしているとき、あなたが言ったことは本当です。しかし、コンパイラはinline、共有ライブラリを構築するときに単に無視することはできません!
DOC

4

最適化中に、マークを付けていなくても、多くのコンパイラーは関数をインライン化します。通常、関数自体をインラインとしてマークする必要があるのは、それ自体が通常正しい判断を下せるため、コンパイラーが知らないことを知っている場合のみです。


多くのコンパイラもこれを行いません。たとえば、MSVCは、指示しない限りこれを行いません
paulm

4

inline#include1つの定義ルールに違反することなく、関数定義をヘッダーファイルに配置し、そのヘッダーファイルを複数のソースファイルに配置できます。


3

一般的に言えば、最近のコンパイラーが何かをインライン化することを心配している今日では、かなりの時間の無駄です。コンパイラーは、コードの独自の分析とコンパイラーに渡される最適化フラグの指定を通じて、これらすべての考慮事項を実際に最適化する必要があります。速度を重視する場合は、速度を最適化するようコンパイラーに指示してください。スペースが気になる場合は、スペースを最適化するようコンパイラーに指示してください。ほのめかされた別の答えとして、まともなコンパイラーは、それが本当に理にかなっている場合、自動的にインライン化することさえします。

また、他の人が述べたように、インラインを使用しても、何もインラインであるとは限りません。保証したい場合は、インライン関数の代わりにマクロを定義する必要があります。

インクルードを強制するマクロをいつインライン化および/または定義するのですか?-アプリケーションの全体的なパフォーマンスに影響を与えることがわかっているコードの重要なセクションの速度が実証され、必要であることが証明されている場合のみ。


…スペースに関心がある場合は、コンパイラーにスペースを最適化するように指示します - 速度を最適化するようにコンパイラーに指示すると、C ++およびCのバイナリーが小さくなります。同様に、コンパイラーにスペースを最適化するように指示すると、実行速度が速くなります。つまり、これらの機能は必ずしも宣伝どおりに機能するとは限りません。人間は、コンパイラーの一般化された解釈(これも使用可能な高速を維持する必要がある)よりも、プログラムのいくつかの側面をよりよく理解する能力を持っています。人間の介入は悪いことである必要はありません。
2013

3

パフォーマンスだけがすべてではありません。C ++とCの両方が、ハードウェアの上にある組み込みプログラミングに使用されます。たとえば、割り込みハンドラーを作成する場合、追加のレジスターやメモリーページをスワップすることなく、コードを一度に実行できることを確認する必要があります。インラインが便利になるのはそのときです。優れたコンパイラは、速度が必要なときに「インライン化」をいくつか行いますが、「インライン化」はそれらを強制します。


1

関数をライブラリにインライン化することで、同じ問題が発生しました。インライン化された関数がライブラリにコンパイルされていないようです。その結果、実行可能ファイルがライブラリのインライン関数を使用したい場合、リンカは「未定義の参照」エラーを出力します。(私に起こったのは、Qtソースをgcc 4.5でコンパイルすることです。


1

デフォルトですべての関数をインライン化しないのはなぜですか?それはエンジニアリングのトレードオフだからです。「最適化」には少なくとも2つのタイプがあります。プログラムの高速化とプログラムのサイズ(メモリフットプリント)の縮小です。インライン化は、一般的に物事をスピードアップします。これは、関数呼び出しのオーバーヘッドを取り除き、スタックからのパラメーターのプッシュとプルを回避します。ただし、すべての関数呼び出しを関数の完全なコードに置き換える必要があるため、プログラムのメモリフットプリントも大きくなります。物事をさらに複雑にするために、CPUは頻繁に使用されるメモリのチャンクをCPU上のキャッシュに保存し、超高速アクセスを実現します。プログラムのメモリイメージを十分に大きくすると、プログラムはキャッシュを効率的に使用できなくなり、最悪の場合、インライン化によってプログラムの速度が実際に低下する可能性があります。


1

私たちのコンピューターサイエンスの教授は、c ++プログラムでインラインを使用しないように促しました。理由を尋ねられたとき、彼は親切に私たちに親切に説明しました。現代のコンパイラーはいつインラインを自動的に使用するかを検出するべきです。

したがって、はい、インラインは可能な限り使用する最適化手法となる可能性がありますが、明らかにこれは、関数をインライン化できる場合はいつでも既に行われていることです。


5
あなたの教授は残念ながら完全に間違っています。inlineC ++には2つの非常に異なる意味があります。そのうちの1つだけが最適化に関連していて、教授はそれに関して正しいです。ただし、の2番目の意味inlineは、多くの場合、1つの定義ルールを満たすために必要です。
Konrad Rudolph

-1

ここでの別の議論からの結論:

インライン関数の欠点はありますか?

どうやら、インライン関数の使用には何の問題もありません。

しかし、次の点に注意する価値があります!

  • インライン化を使いすぎると、実際にはプログラムが遅くなる可能性があります。関数のサイズによっては、関数をインライン化すると、コードサイズが増減する場合があります。非常に小さなアクセサ関数をインライン化すると、通常、コードサイズが小さくなりますが、非常に大きな関数をインライン化すると、コードサイズが劇的に大きくなります。現代のプロセッサでは、命令キャッシュの使用効率が高いため、通常、小さいコードの方が高速に実行されます。-Googleガイドライン

  • インライン関数の速度の利点は、関数のサイズが大きくなるにつれて低下する傾向があります。ある時点で、関数呼び出しのオーバーヘッドが関数本体の実行に比べて小さくなり、利点が失われます-ソース

  • インライン関数が機能しない状況はほとんどありません。

    • 値を返す関数の場合。returnステートメントが存在する場合。
    • 値を返さない関数の場合。ループ、スイッチ、またはgotoステートメントが存在する場合。
    • 関数が再帰的である場合。-ソース
  • この__inlineキーワードを使用すると、optimizeオプションを指定した場合にのみ関数がインライン化されます。optimizeが指定されている場合、それ__inlineが認められるかどうかは、インラインオプティマイザーオプションの設定によって異なります。デフォルトでは、オプティマイザが実行されるときは常にインラインオプションが有効です。optimizeを指定した場合、__inlineキーワードを無視するにはnoinlineオプションも指定する必要があります。-ソース


3
インラインがコマンドであり、コンパイラへのヒントではなかった場合、trueになります。コンパイラーは実際にインライン化するものを決定します。
マーティンヨーク

1
@LokiAstariインラインがコンパイラへのリクエストであることを知っています。私の主張は、コンパイラへのヒントであれば、コンパイラに任せて何が最適かを判断することです。なぜインラインを使用するのにインラインを使用するのか、それでも最終決定を下すのはコンパイラーです。マイクロソフトが_forceinlineを導入したのも不思議です。
クリシュナオザ14

@krish_oza:私のコメントはこちら。この答えについてです。ここでの答えは完全に間違っています。コンパイラーは、コードをインライン化するかどうかを決定するためinlineキーワード無視するため、上記のすべての点が間違っています。コンパイラーがキーワードをインライン化に使用した場合、それらは真実です(リンクする目的で複数の定義をマークすることを決定するためにのみ使用されます)。
マーティンヨーク
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.