web-dev-qa-db-ja.com

ラムダはC ++の関数のようにインライン化されていますか?

コンパイラーは、単純な標準関数のように、ラムダ関数をインライン化して効率を高めることができますか?

例えば.

std::vector<double> vd;
std::for_each(vd.begin(), vd.end(), [](const double d) {return d*d;});

または、最適化の欠如によって効率が低下しますか?

2番目の質問:使用するコンパイラーがアルゴリズムに送信されるインライン関数の呼び出しを最適化したかどうかを確認できる場所つまり、関数オブジェクトではなく関数がアルゴリズムに送信され、最後の関数が関数へのポインターを取得し、インライン関数へのポインターを最適化するコンパイラーとそうでないコンパイラーがあります。

43

あなたの例のような単純な場合では、関数ポインタよりもラムダの方がパフォーマンスが向上することを期待する必要があります、を参照してください

なぜラムダは単純な関数よりもコンパイラによって最適化できるのですか?

他の人がすでに指摘しているように、呼び出しがインライン化されるという保証はありませんが、ラムダを使用する可能性は高くなります。呼び出しがインライン化されているかどうかを確認する1つの方法は、生成されたコードを確認することです。 gccを使用している場合は、-Sフラグをコンパイラーに渡します。もちろん、アセンブリコードを理解できることを前提としています。



2018年9月11日に更新:Vipul Kumar 編集で2つのコンパイラフラグを指摘しました。

GCC -Winline

インラインとして宣言されている関数をインライン化できない場合に警告します。このオプションを使用しても、コンパイラはシステムヘッダーで宣言された関数のインライン化の失敗について警告しません。

コンパイラは、さまざまなヒューリスティックを使用して、関数をインライン化するかどうかを決定します。たとえば、コンパイラーは、インライン化される関数のサイズと、現在の関数で既に行われているインライン化の量を考慮します。そのため、ソースプログラムで一見取るに足りない変更によって、-Winlineによって生成される警告が表示または非表示になる可能性があります。

私がこれを理解しているように、関数がインラインで宣言されていない場合、このコンパイラフラグはおそらくnot役に立つでしょう。それにもかかわらず、それが存在することを知っているのは良いことであり、部分的にあなたの2番目の質問に答えます。

彼が指摘した他のフラグは次のとおりです。

Clang -Rpass=inline

最適化レポートを発行するオプション

最適化レポートは、コンパイラー変換によって行われたすべての主要な決定を高レベルでトレースします。例えば、インライナーが関数foo()をbar()にインライン化することを決定した場合[...]

私はこれを自分では使用していませんが、ドキュメントに基づいて、ユースケースに役立つかもしれません。

生成されたアセンブリは、それが重要である場合は常に個人的にチェックします。

34
Ali

まず、C++でのラムダの設計のポイントは、関数呼び出しに比べてオーバーヘッドがないことです。これには、それらへの呼び出しをインライン化できるという事実が含まれます。

しかし、ここには概念の混乱があります。C++標準では、 「インライン」は関数のlinkageです。つまり、ステートメントです。関数の呼び出し方法ではなく、defined についてインラインで定義された関数canは、そのような関数の呼び出しがインライン化されるコンパイラー最適化の恩恵を受けます。それは異なるが、非常に関連する概念です。

ラムダの場合、呼び出される実際の関数は、ラムダ用のコンパイラによって作成された匿名クラスでinlineとして暗黙的に定義されるメンバoperator()です。ラムダの呼び出しは、そのoperator()への直接呼び出しに変換されるため、インライン化できます。 別の答えでコンパイラがどのようにラムダ型を作成するかを説明しました

24
Konrad Rudolph

コンパイラに与えられた最適化レベルに依存します。たとえば、これら2つの関数は、意味的には同じです。 1つはC++ 11スタイル、もう1つはCスタイルです。

void foo1 (void)
{
    int arr[100];
    std::generate(std::begin(arr), std::end(arr), [](){return std::Rand()%100;});
}

void foo2 (void)
{
    int arr[100];
    for (int *i = arr; i < arr+100; i++) *i = std::Rand()%100;
}

これをgcc -O4でコンパイルすると、2つの関数で非常に類似した(同一ではないが同等の複雑さの)コードが生成されます。

ただし、最適化されていないコンパイル時にラムダはインライン化されません(また、std :: beginおよびstd :: end呼び出しもインライン化されません)。

そのため、コンパイラーは、現代スタイルのコードを最適化するように求められた場合、最適化で優れた仕事をすることができますが、最適化されていないデバッグビルドでは、この種のコードのパフォーマンスが低下する可能性があります。

12
pconnell