web-dev-qa-db-ja.com

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

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

248

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

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

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

私はこのような状況が検出可能な違いを作っているのを見ることができました:

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);
138
Brian R. Bondy

長所

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

短所

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

インラインマジック

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

古風な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ファイルに含めることに注意してください。これにより、afunc()の2つのインスタンスが生成されます。リンカはそれらの1つを破棄します。 inlineが指定されていない場合、リンカはエラーを出します。

41

インライン化はコンパイラーへの提案であり、自由に無視できます。少量のコードに最適です。

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

また、CPUがパイプライン処理をサポートするのは、呼び出しによる新しい命令でパイプラインをリロードする必要がないためです。

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

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

16
paxdiablo

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

利点:-

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

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

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

  4. 命令キャッシュを利用することにより、参照の局所性を高めます。

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

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

12
TSS

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

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

6
doc

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

4
Devrin

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

4
Ferruccio

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

3
J.M. Stoorvogel

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

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

組み込みを強制するマクロをインライン化および/または定義するタイミング-アプリケーションの全体的なパフォーマンスに影響を与えることが知られているコードの重要なセクションに対して、実証済みで必要な実証済みの速度の向上がある場合のみ。

3
Tall Jeff

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

はい、インラインは可能な限り最適化手法として使用できますが、どうやら関数をインライン化できる場合はいつでも既に行われているようです。

1
HumbleWebDev

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

1
Martin Wilde

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

1