2016-05-04 17 views
1

Ich schrieb den folgenden Benchmark den Overhead von virtuellen Funktionen zu schätzen:Virtuelle Funktion Overhead von negativen 0,6ns pro Anruf?

struct A{ 
    int i = 0 ; 
    virtual void inc() __attribute__((noinline)); 
}; 

#ifdef VIRT 
    struct B : public A{ 
    void inc() override __attribute__((noinline)); 
    }; 

    void A::inc() { } 
    void B::inc() { i++; } 
#else 
    void A::inc() { i++; } 
#endif 

int main(){ 
#ifdef VIRT 
    B b; 
    A* p = &b; 
#else 
    A a; 
    A* p = &a; 
#endif 
    for(;p->i < IT; p->inc()) {; } 
    return 0; 
} 

ich es mit

G=$((1000**3)) 
g++ -O1 -DIT=$((1*G)) -DVIRT virt.cc -o virt 
g++ -O1 -DIT=$((1*G)) virt.cc -o nonvirt 

Und die Ergebnisse, die ich waren bekam kompilieren, dass nonvirt über 0.6ns als virt langsamer war per Funktionsaufruf bei -O1 und etwa 0,3ns langsamer als virt bei -O2 pro Funktionsaufruf.

Wie ist das möglich? Ich dachte, virtuelle Funktionen sollten langsamer sein.

Antwort

4

Erstens, nur weil Sie eine Methode über einen Zeiger aufrufen, heißt das nicht, dass der Compiler den Zieltyp nicht herausfinden und den Aufruf nicht virtuell machen kann. Außerdem macht dein Programm nichts anderes, also wird alles gut vorhergesagt und im Cache gespeichert. Schließlich ist ein Unterschied von 0,3 ns ein Zyklus, was kaum erwähnenswert ist. Wenn Sie sich wirklich damit beschäftigen möchten, können Sie den Assembler-Code für jeden Fall auf Ihrer Plattform überprüfen. Auf meinem System (Clang, OS X, altes Macbook Air) ist der virtuelle Fall ein wenig langsamer, aber er ist kaum messbar mit -O1 (z. B. 3,7 vs. 3,6 Sekunden für nicht-virtuelle). Und mit -O2 gibt es keinen Unterschied, den ich unterscheiden kann.

1

EDIT: Hat

Ihr Haupt ist falsch korrigiert. Die for-Schleife ist in einem Fall zweimal und einmal in der anderen definiert. Dies sollte sich nicht auf die Performance auswirken, da die Schleife das zweite Mal sofort beendet wird.

Correct es wie folgt aus:

int main(){ 
#ifdef VIRT 
    B b; 
    A* p = &b; 
    /* removed this for loop */ 
#else 
    A a; 
    A* p = &a; 
#endif 
    for(;p->i < IT; p->inc()) {; } 
    return 0; 
} 
+0

Richtig. Dieser Loop war ein Tippfehler, der glücklicherweise keinen Einfluss auf das Ergebnis der Benchmark hat. – PSkocik