小さい問題の大きい思考のC++の中のinline関数

3541 ワード

inline、不思議なキーワードです.これにより、関数とマクロの利点を同時に得ることができます.inlineで定義された関数は、inlineのない関数よりも関数呼び出しを実行する負担がないため(これは「C++プログラムのメモリレイアウト」を参照)、効率的である.マクロよりも、関数の予想可能な動作とパラメータタイプチェックがあります.マクロの動作は予想しにくいので、次のマクロ定義を見てみましょう.
#define max(a, b) ( (a) > (b) ? (a) : (b) )

int a = 5, b = 0;
max(++a, b); // a = a + 2
max(++a, b+10); // a = a + 1

この場合:
inline int max(int a, int b)
{
     return a > b ? a : b;
}

int a = 5, b = 0;
max(++a, b); // a = a + 1
max(++a, b+10); // a = a + 1

すべてが素晴らしい!でもこんなに簡単なの?
C++がinlineを最初に導入したのは,クラスのパッケージを破壊したくないとともに,効率を維持したためである.例:
class stack {
private:
  int i;
  
public:
  int get() {return i;} // inline  
}; 

stackのメンバー変数iにアクセスしたい場合は、stackのパッケージを維持しながら、呼び出し時に効率的にしたい場合はinlineをお願いします.
inlineはコンパイラにとって「コンパイル段階で呼び出し動作を呼び出し関数の本体に置き換える」ことを意味する.しかし、コンパイラはやることができても、やらなくてもいいというアドバイスにすぎません.論理的に言えば、コンパイラは関数inlineを次のようにします.
1、inline関数体をinline関数呼び出し点にコピーする.
2、使用するinline関数の局部変数にメモリを割り当てる.
3、inline関数の入力パラメータと戻り値を呼び出し方法の局所変数空間にマッピングする.
4.inline関数に複数の戻り点がある場合は、それをinline関数コードブロックの末尾のブランチに変換する(GOTOを使用する).
以上の処理により、呼び出しに関連するすべての痕跡および性能の損失を除去することができる.inlineは、呼び出しオーバーヘッドを排除することによってパフォーマンスを向上させ、呼び出し間の最適化を可能にする.次のコードを見てみましょう.
int test(){
  int a = 6;
  ...... //         a    
  int b = inline_func(b);
  ...... //         b    
  int c = b + 1;
  ......
}

inline int inline_func(int q) {
  if (q > 10) return -1;
  else if (q > 0) return (1 << q) - 1;
  else return 0;
}
inline後
int test() {
  int a = 6;
  ...... //         a    
  int b;
  {
    int _temp_q = 6;
    int _temp;
    if (_temp_q > 10) _temp = -1;
    else if (_temp_q > 0) _temp = (1 << q) - 1;
    else _temp = 0;
    b = _temp;
  }
  ...... //         b    
  int c = b + 1;
  ......
}
最適化後
int test(){
  int a = 6;
  ...... //         a    
  int b = 0x3f;
  ...... //         b    
  int c = 0x40;
  ......
}

上では主にinline関数の長所を述べましたが、inline関数の欠点は何ですか?見てみましょう
1、コード膨張.inline関数体が大きすぎてコンパイラがinlineを成功させると、最終的なプログラムはコードが膨張し、デバイスバッファヒット率が低く、ページエラーが多くなり、ハードディスクを読み書きする回数が多くなり、プログラムの性能が低下します.推奨:inline関数体は一般的に5行を超えないでください.ループは含まれません.再帰呼び出しは含まれません.
2、inline関数の内部にstatic変数はありません.inline関数の定義はほとんどヘッダファイル(.h)に置かれており、複数の実装ファイル(.cpp)を参照することができます.コンパイラが別々にコンパイルされていることを知っていますので、このとき、複数の実装ファイルに複数のinline関数の展開があります.つまり、複数のstatic変数があります.これはおそらく私たちが望んでいるものではありません.
3、inline関数は関数ライブラリのアップグレードに伴ってアップグレードできません.fが関数ライブラリのinline関数である場合、使用するユーザーはf関数エンティティをプログラムにコンパイルします.関数ライブラリ実装者がfを変更すると、fを使用するすべてのプログラムが再コンパイルされる必要があります.fがnon-inlineの場合、ユーザプログラムは再接続するだけでよい.関数ライブラリが動的接続を採用している場合、このアップグレードされたf関数は知らず知らずのうちにプログラムによって使用されます.
4、inline関数のアドレスを取得しないでください.inline関数のアドレスを取得するには、コンパイラが関数エンティティを生成する必要があります.いずれにしても、コンパイラは「関数が存在しない」ポインタを渡すことはできません.クラスのconstructorsとdestructorsの関数ポインタを使用してclassオブジェクトの配列を構築および解析するコンパイラもあります.またクラスのconstructorsとdestructorsは簡単かもしれませんが、その親クラスのconstructorsとdestructorsは複雑かもしれませんので、クラスのconstructorsとdestructorsはinline関数の最適な選択ではありません!
5、inline虚関数は往々にして無効である.虚関数は実行時に決定されることが多いが、inlineはコンパイル時に行われるため、inline虚関数は無効になることが多い.もちろん、クラスのオブジェクトを直接虚関数を使用すると、あるコンパイラにとって最適化の役割を果たすこともできます.
6、inline関数はデバッグできません.理由は、上記のコンパイラが関数をinlineする手順を参照してください.プロジェクトの後期にプログラムをprofileしてから、それらの関数をinline化することを決めてください.参考文献:
1、『C++言語の設計と進化』2.4運行時の効率
2、『Effective C+』条項33
3、《C++性能を高めるプログラミング技術》第8章内聯基礎、第9章内聯-性能の角度に立って、第10章内聯技巧
4、『C++箴言:inline化の介入と排除を理解する』
5、『C++inline関数』