Qual è l’overhead delle prestazioni di std :: function?

Ho sentito su un forum usando std::function causa un calo delle prestazioni. È vero? Se è vero, è un calo notevole delle prestazioni?

Puoi trovare le informazioni dal materiale di riferimento di boost: quanto incorrere in overhead fa una chiamata attraverso boost :: function? e prestazioni

Questo non determina “sì o no” per potenziare la funzione. Il calo delle prestazioni potrebbe essere accettabile in base ai requisiti del programma. Più spesso, le parti di un programma non sono critiche dal punto di vista delle prestazioni. E anche allora potrebbe essere accettabile. Questo è solo qualcosa che puoi determinare.

Per quanto riguarda la versione standard della libreria, lo standard definisce solo un’interfaccia. Spetta interamente alle implementazioni individuali per farlo funzionare. Suppongo che sarebbe stata utilizzata un’implementazione simile alla funzione di boost.

Esistono, infatti, problemi di prestazioni con std:function che deve essere presa in considerazione ogni volta che la si usa. Il principale punto di forza di std::function , ovvero il suo meccanismo di cancellazione del tipo, non viene fornito gratuitamente e potremmo (ma non necessariamente) pagare un prezzo per questo.

std::function è una class template che avvolge i tipi chiamabili. Tuttavia, non è parametrizzato sul tipo chiamabile ma solo sul suo tipo di ritorno e argomento. Il tipo callable è noto solo in fase di costruzione e, quindi, std::function non può avere un membro pre-dichiarato di questo tipo per contenere una copia dell’object dato al suo costruttore.

In parole povere (in realtà, le cose sono più complicate di così) std::function può contenere solo un puntatore all’object passato al suo costruttore, e questo solleva un problema a vita. Se il puntatore punta a un object la cui durata è inferiore a quella dell’object std::function , il puntatore interno diventerà pendente. Per evitare questo problema, std::function potrebbe eseguire una copia dell’object sull’heap attraverso una chiamata operator new (o un allocatore personalizzato). L’allocazione dynamic della memoria è ciò che le persone fanno maggiormente riferimento a una penalizzazione delle prestazioni implicita da std::function .

Ho recentemente scritto un articolo con maggiori dettagli e questo spiega come (e dove) si può evitare di pagare il prezzo di un’allocazione di memoria.

http://drdobbs.com/cpp/232500059

Questo dipende fortemente se si passa la funzione senza vincolare alcun argomento (non alloca lo spazio heap) o meno.

Dipende anche da altri fattori, ma questo è il principale.

È vero che hai bisogno di qualcosa con cui confrontarti, non puoi semplicemente dire che “riduce il sovraccarico” rispetto a non usarlo affatto, devi confrontarlo con un modo alternativo di passare una funzione. E se puoi semplicemente fare a meno di usarlo, allora non era necessario dall’inizio

In primo luogo, il sovraccarico si riduce con l’interno della funzione; maggiore è il carico di lavoro, minore è il sovraccarico.

In secondo luogo: g ++ 4.5 non mostra alcuna differenza rispetto alle funzioni virtuali:

main.cc

 #include  #include  // Interface for virtual function test. struct Virtual { virtual ~Virtual() {} virtual int operator() () const = 0; }; // Factory functions to steal g++ the insight and prevent some optimizations. Virtual *create_virt(); std::function create_fun(); std::function create_fun_with_state(); // The test. Generates actual output to prevent some optimizations. template  int test (T const& fun) { int ret = 0; for (int i=0; i<1024*1024*1024; ++i) { ret += fun(); } return ret; } // Executing the tests and outputting their values to prevent some optimizations. int main () { { const clock_t start = clock(); std::cout < < test(*create_virt()) << '\n'; const double secs = (clock()-start) / double(CLOCKS_PER_SEC); std::cout << "virtual: " << secs << " secs.\n"; } { const clock_t start = clock(); std::cout << test(create_fun()) << '\n'; const double secs = (clock()-start) / double(CLOCKS_PER_SEC); std::cout << "std::function: " << secs << " secs.\n"; } { const clock_t start = clock(); std::cout << test(create_fun_with_state()) << '\n'; const double secs = (clock()-start) / double(CLOCKS_PER_SEC); std::cout << "std::function with bindings: " << secs << " secs.\n"; } } 

impl.cc

 #include  struct Virtual { virtual ~Virtual() {} virtual int operator() () const = 0; }; struct Impl : Virtual { virtual ~Impl() {} virtual int operator() () const { return 1; } }; Virtual *create_virt() { return new Impl; } std::function create_fun() { return []() { return 1; }; } std::function create_fun_with_state() { int x,y,z; return [=]() { return 1; }; } 

Output di g++ --std=c++0x -O3 impl.cc main.cc && ./a.out :

 1073741824 virtual: 2.9 secs. 1073741824 std::function: 2.9 secs. 1073741824 std::function with bindings: 2.9 secs. 

Quindi, non temere. Se il tuo design / manutenibilità può migliorare, preferendo la std::function alle chiamate virtuali, provali. Personalmente, mi piace molto l'idea di non forzare le interfacce e l'ereditarietà sui client delle mie classi.