15

inlineでキーワードを使用するのが好きでC、大きな関数をヘッダーに入れる人もいます。これが効果がないと考えるのはいつですか?珍しいので、時々迷惑だと思います。

私の原則は、inline非常に頻繁にアクセスされる小さな関数、または実際の型チェックを行うために使用する必要があるということです。inlineとにかく、私の好みは私を導きますが、大きな関数にはあまり役に立たない理由を最もよく説明する方法がわかりません。

この質問では、正しいことを推測する際にコンパイラーがより良い仕事をすることができると人々は示唆しています。それは私の推測でもありました。この引数を使用しようとすると、さまざまなオブジェクトからの関数では機能しないという回答が寄せられました。まあ、わかりません(たとえば、GCCを使用しています)。

回答ありがとうございます。

4

12 に答える 12

26

inline次の 2 つのことを行います。

  1. 「1 つの定義規則」 (以下を参照) から免除されます。これは常に適用されます。
  2. 関数呼び出しを回避するためのヒントをコンパイラに提供します。コンパイラはこれを自由に無視できます。

#1 非常に便利です (例: 短い場合はヘッダーに定義を入れる) #2 が無効になっていても。

実際には、コンパイラーは、何をインライン化するかを自分自身でうまく判断できることがよくあります (特に、プロファイルに基づく最適化が利用可能な場合)。


[編集: 完全な参考文献と関連テキスト]

上記の 2 点はいずれも、ISO/ANSI 標準 (ISO/IEC 9899:1999(E)、通称「C99」) に準拠しています。

§6.9「外部定義」のパラグラフ 5:

外部定義は、関数 (インライン定義以外) またはオブジェクトの定義でもある外部宣言です。外部リンケージで宣言された識別子が式で使用される場合 (結果が整数定数である sizeof 演算子のオペランドの一部として以外)、プログラム全体のどこかに、識別子の外部定義が 1 つだけ存在する必要があります。それ以外の場合は、1 つしか存在しないものとします。

C++ での同等の定義は明示的に One Definition Rule (ODR) と名付けられていますが、同じ目的を果たします。外部 (つまり、「静的」ではないため、単一の翻訳単位 (通常は単一のソース ファイル) に対してローカル)は、それが関数インラインでない限り、一度だけ定義できます。

§6.7.4、「関数指定子」では、インライン キーワードが定義されています。

関数をインライン関数にすることは、関数の呼び出しが可能な限り高速であることを示唆しています。[118]そのような提案が有効である範囲は実装定義です。

および脚注 (非規範的) ですが、明確化を提供します。

たとえば、「インライン置換」など、通常の関数呼び出しメカニズムの代替手段を使用します。インライン置換はテキスト置換ではなく、新しい関数も作成しません。したがって、たとえば、関数の本体内で使用されるマクロの展開では、関数が呼び出される場所ではなく、関数の本体が表示される時点での定義が使用されます。識別子は、本文が発生するスコープ内の宣言を参照します。同様に、関数は、外部定義に加えて発生するインライン定義の数に関係なく、1 つのアドレスを持ちます。

要約: C および C++ のほとんどのユーザーがインラインに期待するものは、得られるものではありません。関数呼び出しのオーバーヘッドを回避するという明らかな主な目的は、完全にオプションです。ただし、個別のコンパイルを可能にするには、単一の定義を緩和する必要があります。

(標準からの引用はすべて強調されています。)


編集2:いくつかのメモ:

  • 外部インライン関数にはさまざまな制限があります。関数に静的変数を含めることはできず、静的 TU スコープ オブジェクト/関数を参照することはできません。
  • VC++ の「プログラム全体の最適化」でこれを見たところです。これは、作成者ではなく独自のインライン処理を行うコンパイラの例です。
于 2009-02-24T18:25:27.260 に答える
5

大きな関数にインラインを使用すべきでないもう1つの理由は、ライブラリの場合です。インライン関数を変更するたびに、古いヘッダーに対してコンパイルされたアプリケーションが古いバージョンの関数をインライン化したままであるため、ABIの互換性が失われる可能性があります。インライン関数をタイプセーフマクロとして使用する場合、ライブラリのライフサイクルで関数を変更する必要がない可能性が高くなります。しかし、大きな機能の場合、これを保証するのは困難です。

もちろん、この引数は、関数がパブリックAPIの一部である場合にのみ適用されます。

于 2009-02-25T09:56:35.683 に答える
5

インライン宣言の重要な点は、必ずしも何もしないということです。コンパイラは、多くの場合、宣言されていない関数をインライン展開するか、インライン宣言されている関数をリンクするかを自由に決定できます。

于 2009-02-24T18:26:41.423 に答える
5

インラインの利点を示す例。sinCos.h :

int16 sinLUT[ TWO_PI ]; 

static inline int16_t cos_LUT( int16_t x ) {
    return sin_LUT( x + PI_OVER_TWO )
}

static inline int16_t sin_LUT( int16_t x ) {
    return sinLUT[(uint16_t)x];
}

重い数の処理を行い、sin/cos の計算で無駄なサイクルを避けたい場合は、sin/cos を LUT に置き換えます。

インラインなしでコンパイルすると、コンパイラはループを最適化せず、出力 .asm に次の行に沿って何かが表示されます。

;*----------------------------------------------------------------------------*
;*   SOFTWARE PIPELINE INFORMATION
;*      Disqualified loop: Loop contains a call
;*----------------------------------------------------------------------------*

inline でコンパイルすると、コンパイラはループで何が起こっているかを把握しており、何が起こっているかを正確に把握しているため、最適化を行います。

出力 .asm には、最適化された「パイプライン化された」ループがあります (つまり、すべてのプロセッサの ALU を完全に利用しようとし、NOPS なしでプロセッサのパイプラインをフルに維持しようとします)。


この特定のケースでは、パフォーマンスを約 2 倍または 4 倍向上させることができ、リアルタイムの締め切りに必要な範囲内に収めることができました。


PS私は固定小数点プロセッサで作業していました...そしてsin/cosのような浮動小数点演算は私のパフォーマンスを殺しました.

于 2009-02-24T21:27:42.253 に答える
4

関数へのポインターを使用する場合、インラインは効果がありません。

于 2009-02-24T18:26:17.073 に答える
3

インラインは1つのケースで効果的です。パフォーマンスの問題が発生した場合、実際のデータを使用してプロファイラーを実行し、いくつかの小さな関数の関数呼び出しのオーバーヘッドが重要であることがわかりました。

それ以外では、なぜあなたがそれを使うのか想像できません。

于 2009-02-24T19:07:13.200 に答える
2

私は主にインライン関数をタイプセーフマクロとして使用しています。特にLLVMが登場して以来、かなり長い間、リンク時の最適化のサポートをGCCに追加することについての話がありました。しかし、実際にどれだけ実装されているかはまだわかりません。

于 2009-02-24T18:28:30.030 に答える
2

それは正しい。大きな関数にインラインを使用すると、コンパイル時間が長くなりますが、アプリケーションのパフォーマンスはほとんど向上しません。インライン関数は、呼び出しなしで関数が含まれることをコンパイラに伝えるために使用されます。これは、何度も繰り返される小さなコードである必要があります。言い換えれば、大きな関数の場合、独自の関数実装のコストと比較して、呼び出しを行うコストはごくわずかです。

于 2009-02-24T18:26:58.443 に答える
2

インラインは、getter メソッドや setter メソッドなど、小さくて頻繁に使用される関数に使用できます。大きな関数の場合、exe のサイズが大きくなるため、インラインを使用することはお勧めできません。また、再帰関数の場合、インライン化してもコンパイラは無視します。

于 2009-02-24T18:27:30.907 に答える
2

個人的には、最初にコードでプロファイラーを実行し、そのルーチンにインライン化によって部分的に軽減できる重大なボトルネックがあることを証明しない限り、インライン化すべきはないと思います。

これは、Knuth が警告した早すぎる最適化のもう 1 つのケースです。

于 2009-02-24T19:20:23.713 に答える
1
  1. inlineヒントとしてのみ機能します。
  2. ごく最近追加されました。そのため、最新の標準準拠コンパイラでのみ動作します。
于 2009-02-24T18:26:16.543 に答える
1

インライン関数は、選択したコンパイラに応じて、約 10 行以下にする必要があります。

何かをインライン化する必要があることをコンパイラに伝えることができます..そうするのはコンパイラ次第です。私が知っているコンパイラが無視できない -force-inline オプションはありません。そのため、アセンブラーの出力を見て、コンパイラーが実際に関数をインライン化したかどうかを確認する必要があります。多くのコンパイラは、黙って「めちゃめちゃ!」と言うだけです。その観点において。

したがって、次の場合:

static inline unsigned int foo(const char *bar)

.. static int foo() 最適化(およびおそらくループ)を再検討したり、コンパイラーと議論したりする時間よりも物事を改善しません。それを開発した人ではなく、最初にコンパイラと議論するように特別な注意を払ってください..または、翌日受信トレイを開いたときに多くの不快な読書を待っているだけです.

一方、インラインで何かを作成する (または何かを作成しようとする) 場合、実際にインライン化が正当化されるのでしょうか? 呼び出されるたびにその関数を拡張したいですか? ジャンプはそれほどコストがかかりますか? コンパイラは通常 9/10 回正しいです。中間出力 (または asm ダンプ) を確認してください。

于 2009-02-25T12:28:05.897 に答える