Wie hoch ist der performancesaufwand von std :: function?

Ich hörte in einem Forum mit std::function verursacht performancesabfall. Ist es wahr? Wenn es wahr ist, ist es ein großer performancesabfall?

Sie können Informationen aus den Referenzmaterialien des Boost finden: Wie viel Overhead kostet ein Aufruf durch boost :: function? und performance

Dies bestimmt nicht “Ja oder Nein”, um die function zu verstärken. Der performancesabfall kann angesichts der Anforderungen des Programms durchaus akzeptabel sein. Meistens sind Teile eines Programms nicht leistungskritisch. Und selbst dann kann es akzeptabel sein. Das können Sie nur bestimmen.

Was die Version der Standardbibliothek betrifft, definiert der Standard nur eine Schnittstelle. Es liegt an einzelnen Implementierungen, damit es funktioniert. Ich nehme an, dass eine ähnliche Implementierung der Boost-function verwendet wird.

Es gibt tatsächlich performancesprobleme mit der std:function , die bei der Verwendung berücksichtigt werden muss. Die Hauptstärke von std::function , nämlich der Typ-Lösch-Mechanismus, ist nicht kostenlos, und wir müssen (aber müssen nicht unbedingt) dafür einen Preis zahlen.

std::function ist eine Vorlagenklasse, die aufrufbare Typen umschließt. Es ist jedoch nicht auf den aufrufbaren Typ selbst parametrisiert, sondern nur auf seine Rückkehr und Argumenttypen. Der aufrufbare Typ ist nur zur Konstruktionszeit bekannt und deshalb kann std::function kein vor-deklariertes Element dieses Typs haben, um eine Kopie des Objekts, das seinem Konstruktor übergeben wurde, zu halten.

Grob gesagt (eigentlich sind die Dinge komplizierter als das) kann std::function nur einen pointers auf das Objekt halten, das an seinen Konstruktor übergeben wurde, und dies wirft ein lebenslanges Problem auf. Wenn der pointers auf ein Objekt zeigt, dessen Lebensdauer kleiner ist als die des std::function Objekts, wird der innere pointers schwankend. Um dieses Problem zu vermeiden, erstellt std::function möglicherweise eine Kopie des Objekts auf dem Heap über einen Aufruf an operator new (oder einen benutzerdefinierten Zuordner). Die dynamische Speicherzuweisung wird am häufigsten als performanceseinbuße bezeichnet, die von std::function impliziert wird.

Ich habe kürzlich einen Artikel mit mehr Details geschrieben, der erklärt, wie (und wo) man den Preis einer Speicherzuweisung vermeiden kann.

http://drdobbs.com/cpp/232500059

Dies hängt stark davon ab, ob Sie die function übergeben, ohne ein Argument zu binden (es wird kein Heap-Speicher zugewiesen) oder nicht.

Kommt auch auf andere Faktoren an, aber das ist der Hauptgrund.

Es ist wahr, dass Sie etwas benötigen, mit dem Sie vergleichen können, Sie können nicht einfach sagen, dass es den Overhead reduziert, verglichen damit, dass Sie es überhaupt nicht verwenden. Sie müssen es mit einem alternativen Weg vergleichen, eine function zu übergeben. Und wenn Sie nur auf die Verwendung verzichten können, dann war es von Anfang an nicht nötig

Erstens wird der Overhead mit dem Inneren der function kleiner; Je höher die Arbeitslast, desto geringer der Overhead.

Zweitens: g ++ 4.5 zeigt keinen Unterschied zu virtuellen functionen:

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; }; } 

Ausgabe von 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. 

Also, fürchte dich nicht. Wenn sich Ihr Design / Ihre Wartbarkeit von der Bevorzugung von std::function über virtuelle Aufrufe verbessern lässt, probieren Sie sie aus. Persönlich mag ich die Idee, Interfaces und inheritance auf Clients meiner classn nicht zu erzwingen.