2013-06-12 18 views
7

Le funzioni lambda ricorsive inducono qualsiasi overhead a confronto con le funzioni ricorsive regolari (poiché dobbiamo acquisirle in una funzione std ::)?
Qual è la differenza tra questa funzione e una simile utilizzando solo le funzioni regolari?Sovraccarico di lambda ricorsivo

int main(int argc, const char *argv[]) 
{ 
    std::function<void (int)> helloworld = [&helloworld](int count) { 
     std::cout << "Hello world" << std::endl; 
     if (count > 1) helloworld(--count); 
    }; 
    helloworld(2); 
    return 0; 
} 
+2

Il tuo problema principale sarà una minore ottimizzazione del compilatore http://ideone.com/QsZVfH – stefan

+0

Bello! Hai un'idea del perché? Il compilatore non ha potuto solo intuire che questo codice è simile a una normale chiamata di funzione (no-capture ma stessa)? – 3XX0

+0

@fscan Devo perché è una funzione lambda ricorsiva. L'inferenza di tipo non funziona qui – 3XX0

risposta

4

C'è sovraccarico utilizzando lambda ricorsivamente memorizzando come std::function, anche se sono fondamentalmente stessa funtori. Sembra che gcc non sia in grado di ottimizzare bene il che può essere visto in diretta comparison.

L'implementazione del comportamento di una lambda, ovvero la creazione di un functor, abilita nuovamente l'ottimizzazione gcc. Il tuo esempio specifico di un lambda potrebbe essere implementato come

struct HelloWorldFunctor 
{ 
    void operator()(int count) const 
    { 
     std::cout << "Hello world" << std::endl; 
     if (count > 1) 
     { 
     this->operator()(count - 1); 
     } 
    } 
}; 
int main() 
{ 
    HelloWorldFunctor functor; 
    functor(2); 
} 

Per l'esempio ho creato il funtore sarebbe simile in questo second demo.

Anche se si introduce chiamate a funzioni quali impuri std::rand, le prestazioni senza un ricorsiva lambda o con un funtore personalizzato è ancora meglio. Ecco uno third demo.

Conclusione: con l'utilizzo di un std::function, c'è un sovraccarico, anche se potrebbe essere trascurabile a seconda del caso d'uso. Poiché questo utilizzo impedisce alcune ottimizzazioni del compilatore, questo non dovrebbe essere ampiamente utilizzato.

+0

è vero, grazie per il codice, anche se ho il sospetto che sia molto specifico per il compilatore. C'è un modo per passare i flag del compilatore su ideone? – Pedrom

+0

@Pedrom Non credo. ter? Disclaimer: Non ho ancora utilizzato intensivamente lambda perché i progetti su cui lavoro necessitano di compatibilità con i compilatori che non supportano lambda. – stefan

+0

Sto provando a convalidarlo sulla mia macchina, ma per qualche motivo non mi mostra alcun output usando cygwin:/ – Pedrom

2

lambda in C++ sono equivalenti a funtori quindi sarebbe la stessa che chiama l'operatore() di una classe creata automaticamente dal compilatore. Quando si cattura l'ambiente, ciò che accade dietro le quinte è che quelle variabili catturate vengono passate al costruttore della classe e memorizzate come variabili membro.

Quindi, in breve, la differenza di prestazioni dovrebbe essere molto vicina allo zero.

Qui c'è qualche ulteriore spiegazione: "Come sono Lambda Chiusure implementato"

Vai alla sezione http://www.cprogramming.com/c++11/c++11-lambda-closures.html

EDIT:

Dopo alcune ricerche e grazie a Stefan risposta e il codice, si è scoperto che v'è un sovraccarico sulla ricorsive lambda a causa della std :: funzione di linguaggio. Poiché lambda deve essere incapsulato su una funzione std :: per chiamare se stesso, si tratta di chiamare una funzione virtuale che compie aggiungendo un sovraccarico.

Il soggetto viene trattato sulle osservazioni di questa risposta: https://stackoverflow.com/a/14591727/1112142

+0

Stephan Lavavej presenta l'idea simile [qui] (http://channel9.msdn.com/Series/C9-Lectures-Stephan-T-Lavavej-Core-C-/Stephan-T-Lavavej-Core-C-9- di-n) (alle 0:38:20). Sembra quindi che il richiamo di una funzione regolare induca meno spese generali. Tuttavia, ricordo di aver letto da qualche parte che questo approccio del functor poteva essere ottimizzato con i puntatori di funzione in alcuni casi. È vero ? per quanto riguarda il caso che ho fornito? – 3XX0

+0

@ 3XX0 Questo è un bel video, grazie per il link :) Comunque ha detto che se hai intenzione di usarlo più volte avrebbe senso scrivere una funzione regolare, ma è solo perché è più facile chiamare in diverse parti di il codice, non sta parlando di prestazioni. Per quanto riguarda la funzione puntatori di funzione, potresti usarli * invece di * lambda, ma non penso che saranno così convenienti e in pratica non ho visto alcuna differenza di prestazioni. – Pedrom

+0

@ 3XX0 Come per il tuo esempio, è esattamente come chiamare una funzione void hello_world (myfunc foo, int count) se c'è * qualsiasi * overhead che sarebbe alla prima chiamata (perché devi istanziare un oggetto foo per passarlo al metodo, ma in seguito tutte le seguenti chiamate sono esattamente le stesse in termini di prestazioni.Inoltre, se la tua funzione ricorsiva è un metodo, allora non c'è alcuna differenza – Pedrom

3

Quindi std::function viene implementato in modo polimorfico. Significato il codice è più o meno equivalente a:

struct B 
{ 
    virtual void do(int count) = 0; 
}; 

struct D 
{ 
    B* b; 
    virtual void do(int count) 
    { 
     if (count > 1) 
      b->do(count--); 
    } 
}; 

int main() 
{ 
    D d; 
    d.b = &d; 
    d.do(10); 
} 

E 'raro avere una sufficiente ricorsione stretta tale che un metodo di ricerca virtuale è un overhead significativo, ma a seconda del campo di applicazione è certamente possibile.