2013-09-04 13 views
20

これはすでにWhy C++ lambda is slower than ordinary function when called multiple times?C++0x Lambda overhead で触れられていましたが、私の例は前者の議論とは少し異なり、後者の結果と矛盾していると思います。私のコードのボトルネックのための検索でC++でラムダ関数のオーバーヘッドを理解する11

私はバッファに値をコピーするように、与えられたプロセッサ機能付き可変長引数リストを処理recusiveテンプレート関数を発見しました。コンパイル

int buffer[10]; 
int main(int argc, char **argv) 
{ 
    int *p = buffer; 

    for (unsigned long int i = 0; i < 10E6; ++i) 
    { 
    p = buffer; 
    ProcessArguments<int>([&p](const int &v) { *p++ = v; }, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); 
    } 
} 

template <typename T> 
void ProcessArguments(std::function<void(const T &)> process) 
{} 

template <typename T, typename HEAD, typename ... TAIL> 
void ProcessArguments(std::function<void(const T &)> process, const HEAD &head, const TAIL &... tail) 
{ 
    process(head); 
    ProcessArguments(process, tail...); 
} 

私はラムダ関数と同様に移動ポインタを使用してグローバルバッファにコピー引数をグローバル関数と一緒に、このコードを使用するプログラムの実行時間を比較しましたg ++ 4.6の場合、ツール時間の測定はマシンで6秒以上かかりますが、

int buffer[10]; 
int *p = buffer; 
void CopyIntoBuffer(const int &value) 
{ 
    *p++ = value; 
} 

int main(int argc, char **argv) 
{ 
    int *p = buffer; 

    for (unsigned long int i = 0; i < 10E6; ++i) 
    { 
    p = buffer; 
    ProcessArguments<int>(CopyIntoBuffer, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); 
    } 

    return 0; 
} 

の場合は約1.4秒かかります。

私は時間のオーバーヘッドを説明し、私は、実行時に支払うことなく、ラムダ関数を利用するために何かを変更することができるかどうか疑問に思って舞台裏で何が起こっているかを得ることはありません。

+0

だから、グローバルなものがあり、本当に遅く:、予想通り、ラムダ本体をインライン化 注意はあなたに最高のパフォーマンスを与えていますか?ラムダベースでは6対1.4ですが、最後の文は意味がありません。 – dasblinkenlight

+0

分析を行っているときに、生成されたアセンブリリストを調べましたか? – WhozCraig

+0

void processArguments(const std :: function &process) 'のように' process'参照を 'const'参照で渡すでしょうか? – dasblinkenlight

答えて

33

ここでの問題は、std :: functionの使用方法です。 コピーして内容をコピーします(そして、パラメータを巻き戻しながら再帰的に行います)。

今、関数へのポインタのために、内容は、まあ、関数へのポインタです。 ラムダの場合、コンテンツは少なくとも、キャプチャした関数+参照へのポインタです。これはコピーする倍の倍です。さらに、std :: functionの型消去のために、データをコピーするのが最も遅くなる(インライン化されない)。

あり、いくつかのオプションがここにいる、そして最高のは、おそらく代わりに::関数はstdが、テンプレートではない渡すことになります。メリットは、メソッド呼び出しがインライン化される可能性が高く、型の消去がstd :: functionによって行われないこと、コピーが発生しないこと、すべてがとても良いことです。そのような:

template <typename TFunc> 
void ProcessArguments(const TFunc& process) 
{} 

template <typename TFunc, typename HEAD, typename ... TAIL> 
void ProcessArguments(const TFunc& process, const HEAD &head, const TAIL &... tail) 
{ 
    process(head); 
    ProcessArguments(process, tail...); 
} 

2番目のオプションは、同じことをやっているが、コピーによってprocessを送信します。今、コピーが行われますが、まだきれいにインライン化されています。

特に重要なことは、process 'の本体も、特にlamdaの場合にインライン化できることです。ラムダオブジェクトのコピーの複雑さとそのサイズに応じて、コピーを渡すことは、参照渡しよりも高速かもしれません。コンパイラーは、ローカルコピーよりも参照についての時間推論が難しくなる可能性があるので、より速いかもしれません。

template <typename TFunc> 
void ProcessArguments(TFunc process) 
{} 

template <typename TFunc, typename HEAD, typename ... TAIL> 
void ProcessArguments(TFunc process, const HEAD &head, const TAIL &... tail) 
{ 
    process(head); 
    ProcessArguments(process, tail...); 
} 

3番目のオプションは、std :: function <を参照してください。この方法では、少なくともコピーは避けられますが、コールはインライン化されません。

いくつかのperf結果があります(ideonesのC++ 11コンパイラを使用しています)。

Original function: 
0.483035s 

Original lambda: 
1.94531s 


Function via template copy: 
0.094748 

### Lambda via template copy: 
0.0264867s 


Function via template reference: 
0.0892594s 

### Lambda via template reference: 
0.0264201s 


Function via std::function reference: 
0.0891776s 

Lambda via std::function reference: 
0.09s 
+0

興味深い読み物!私はちょうどあなたがテンプレートコピーを介してラムダを示唆したいと思いますか?つまり、参考に何が間違っていますか?コピーを追加する利点がありますか? – guyarad

+0

私は引用以上にコピーを提案しなかった:)私が言ったことは、それらのすべてのオプションがあるということでした。多分それはあまり明確ではなかったでしょう。そして、あなたはどちらが参照(またはコピーによってラムダを渡す)が良いか分からない。ラムダ(そのサイズ)の内容に依存して、その複写の複雑さは、参照渡しがより速くても遅くてもよい。コンパイラは、値渡しのオブジェクトについての参照よりも、参照のための時間推論が難しい場合があります。 – biocomp

+0

私は母国語の話し手ではないかもしれないが、この文章は私を混乱させた。実際に**同じことをすることができる(強調は私のものである)。 「実際に」という言葉は、あなたがこれがより良いということを意味していると思うように導いてくれました。 – guyarad

関連する問題