ラムダの最適化とインライン関数: コンパイラーの利点
ラムダは単純な関数と比較して優れたコンパイラー最適化を示すという Nicolai Josuttis のステートメントは多くの人の興味をそそりました開発者。この主張を調査して、この最適化の利点の背後にある根本的な理由を解明しようとします。
関数オブジェクトとインライン化
関数オブジェクトであるラムダには、重要な利点があります。関数テンプレートは、そのラムダ専用に調整された関数のインスタンス化をトリガーします。これにより、コンパイラーはラムダ呼び出しを簡単にインライン化できます。
対照的に、関数は関数テンプレートに渡されるときに関数ポインターを使用します。従来、コンパイラは関数ポインタを介した呼び出しをインライン化する際に課題に直面していました。インライン最適化は、囲んでいる関数自体がインライン化されている場合にのみ実現可能です。
テンプレートのインスタンス化: 違いを調べる
この違いを説明するには、マップ関数テンプレートを考えてみましょう。
templatevoid map(Iter begin, Iter end, F f) { for (; begin != end; begin) *begin = f(*begin); }
ラムダで呼び出す:
int a[] = { 1, 2, 3, 4 }; map(begin(a), end(a), [](int n) { return n * 2; });
カスタマイズされたインスタンス化が生成されます:
template void map(int* begin, int* end, _some_lambda_type f) { for (; begin != end; begin) *begin = f.operator()(*begin); }
コンパイラーは _some_lambda_type::operator() 関数を識別し、その関数への呼び出しを直接インライン化できます。個別のラムダ型ごとにマップの新しいインスタンス化が要求され、ラムダ固有の最適化が保証されます。
対照的に、関数ポインターを使用してマップを呼び出すと、次のインスタンス化が生成されます:
template void map(int* begin, int* end, int (*f)(int)) { for (; begin != end; begin) *begin = f(*begin); }
ここで、関数ポインタ f はマップ呼び出しごとに異なるアドレスを指し、インライン最適化を禁止しています。コンパイラが f を特定の関数に解決するには、map の呼び出しをインライン化する必要があります。
したがって、関数オブジェクトとしてのラムダの特徴と、テンプレートのインスタンス化を容易にする機能により、従来の関数呼び出しよりも優れた最適化機能がコンパイラに与えられます。ポインタを介して。
免責事項: 提供されるすべてのリソースの一部はインターネットからのものです。お客様の著作権またはその他の権利および利益の侵害がある場合は、詳細な理由を説明し、著作権または権利および利益の証拠を提出して、電子メール [email protected] に送信してください。 できるだけ早く対応させていただきます。
Copyright© 2022 湘ICP备2022001581号-3