Frage

Gibt es eine Laufzeitleistungseinbuße, wenn Schnittstellen (abstrakte Basisklassen) in C ++ verwenden?

War es hilfreich?

Lösung

Kurze Antwort: Nr.

Lange Antwort: Es ist nicht die Basisklasse oder die Anzahl der Vorfahren eine Klasse in ihrer Hierarchie, die ihm Geschwindigkeit auswirkt. Das einzige, was die Kosten für einen Methodenaufruf.

Ein nicht virtueller Methodenaufruf hat Kosten (kann aber inlined werden)
Ein virtueller Methodenaufruf hat einen etwas höheren Kosten als die Methode nachschlagen müssen aufrufen, bevor Sie es nennen (aber das ist eine einfache Tabelle nachschlagen nicht eine Suche). Da alle Methoden auf einer Schnittstelle per definitionem virtuell sind es diese Kosten.

Wenn Sie eine hyper geschwindigkeitsabhängige Anwendung schreiben sollte dies kein Problem sein. Die zusätzliche Klarheit, dass Sie aus über eine Schnittstelle empfangen werden macht in der Regel für jede empfundene Geschwindigkeit Abnahme auf.

Andere Tipps

Funktionen mit virtuellen Dispatch aufgerufen werden nicht inlined

Es ist eine Art Strafe für virtuelle Funktionen, die über leicht zu vergessen ist: virtuelle Anrufe werden in einer (gemeinsamen) Situation nicht inlined wo die Art des Objekts kennt keine Zeit kompilieren. Wenn Ihre Funktion klein und geeignet für inlining ist, kann diese Strafe sehr bedeutsam sein, da Sie nicht nur einen Anruf Overhead hinzugefügt, aber der Compiler wird auch begrenzt, wie es die anrufende Funktion optimieren kann (es hat die virtuelle Funktion kann davon ausgehen, einige Register oder Speicherplatz geändert hat, kann es nicht konstante Werte zwischen dem Anrufer propagieren und den Angerufenen).

Virtuelle Anrufkosten sind abhängig von Plattform

Wie für die Anrufüberkopf Strafe im Vergleich zu einem normalen Funktionsaufruf, hängt die Antwort auf Ihre Zielplattform. Wenn Ihr einen PC mit x86 / x64 CPU abzielen, für die Strafe eine virtuelle Funktion aufruft ist sehr klein, da moderne x86 / x64 CPU Verzweigungsvorhersage auf indirekte Anrufe durchführen können. Wenn Sie jedoch einen PowerPC oder eine andere RISC-Plattform zielen, die virtuelle Call Strafe kann erheblich sein, weil indirekte Anrufe werden nie auf einigen Plattformen (vgl PC / Xbox 360 Cross Platform-Entwicklung Best Practices ).

Es gibt eine kleine Gebühr pro virtuellem Funktionsaufruf im Vergleich zu einem normalen Anruf. Sie werden kaum einen Unterschied beobachten, wenn Sie Hunderttausende von Anrufen pro Sekunde tun, und der Preis ist oft lohnt sich auf jeden Fall für zusätzlichen Code Klarheit zu zahlen.

Wenn Sie eine virtuelle Funktion aufrufen (sagen über eine Schnittstelle) wird das Programm hat einen Blick auf die Funktion in einer Tabelle zu tun, um zu sehen, welche Funktion für das Objekt aufzurufen. Daraus ergibt sich eine kleine Strafe im Vergleich zu einem direkten Aufruf der Funktion.

Auch wenn Sie eine virtuelle Funktion der Compiler verwenden können, den Funktionsaufruf nicht inline. Daher könnte es eine Strafe sein, eine virtuelle Funktion für einige kleine Funktionen zu verwenden. Dies ist in der Regel die größte Leistung „Hit“ Sie sind wahrscheinlich zu sehen. Das ist wirklich nur ein Problem, wenn die Funktion klein und rief oft, sagen wir innerhalb einer Schleife.

Eine weitere Alternative, die in einigen Fällen anwendbar ist, ist Compiler- Polymorphismus mit Vorlagen. Es ist zum Beispiel nützlich, wenn Sie möchten, eine Implementierung Wahl am Anfang des Programms zu machen, und verwenden Sie es dann für die Dauer der Ausführung. Ein Beispiel mit Laufzeit-Polymorphismus

class AbstractAlgo
{
    virtual int func();
};

class Algo1 : public AbstractAlgo
{
    virtual int func();
};

class Algo2 : public AbstractAlgo
{
    virtual int func();
};

void compute(AbstractAlgo* algo)
{
      // Use algo many times, paying virtual function cost each time

}   

int main()
{
    int which;
     AbstractAlgo* algo;

    // read which from config file
    if (which == 1)
       algo = new Algo1();
    else
       algo = new Algo2();
    compute(algo);
}

Die gleiche Zeit Polymorphismus kompilieren

class Algo1
{
    int func();
};

class Algo2
{
    int func();
};


template<class ALGO>  void compute()
{
    ALGO algo;
      // Use algo many times.  No virtual function cost, and func() may be inlined.
}   

int main()
{
    int which;
    // read which from config file
    if (which == 1)
       compute<Algo1>();
    else
       compute<Algo2>();
}

Ich glaube nicht, dass die Kostenvergleich zwischen virtueller Funktionsaufruf ist und eine gerade Funktion aufrufen. Wenn Sie eine abstrakte Basisklasse, über die Verwendung von (Schnittstelle) denken, dann haben Sie eine Situation, wo Sie eine von mehreren Aktionen ausführen wollen anhand des dynamischen Typ eines Objekts. Sie müssen irgendwie diese Wahl treffen. Eine Möglichkeit ist, virtuelle Funktionen zu nutzen. Ein weiterer Grund ist ein Schalter von dem Typ des Objekts, sei es durch RTTI (potentiell teuren), oder einen Typ () Verfahren auf die Basisklasse Addieren (potentiell zunehmenden Speicherverwendung jedes Objekts). So sind die Kosten der virtuellen Funktionsaufruf sollte, um die Kosten der alternativen verglichen werden, nicht zu den Kosten nichts zu tun.

Die meisten Leute beachten Sie die Laufzeitstrafe, und das zu Recht.

Doch meiner Erfahrung bei großen Projekten arbeiten, versetzt die Vorteile von klaren Schnittstellen und korrekte Verkapselung schnell die Verstärkung in der Geschwindigkeit. Modular-Code kann für eine verbesserte Implementierung ausgetauscht werden, so dass das Nettoergebnis eine große Verstärkung ist.

Ihre Leistung kann variieren, und es hängt eindeutig von der Anwendung, die Sie entwickeln.

Eine Sache, die beachtet werden sollte, ist, dass virtuelle Funktionsaufruf Kosten von einer Plattform zur anderen variieren kann. Auf Konsolen können sie stärker spürbar sein, da in der Regel VTable Anruf eine Cache-Miss bedeutet und Verzweigungsvorhersage schraubt.

Beachten Sie, dass die Mehrfachvererbung die Objektinstanz mit mehreren VTable-Zeiger bläht. Mit G ++ auf x86, wenn Ihre Klasse eine virtuelle Methode und keine Basisklasse hat, haben Sie einen Zeiger auf Vtable. Wenn Sie eine Basisklasse mit virtuellen Methoden haben, haben Sie noch einen Zeiger auf Vtable. Wenn Sie zwei Basisklassen mit virtuellen Methoden haben, haben Sie zwei VTable-Zeiger auf jeder Instanz .

So kann mit Mehrfachvererbung (was, was Schnittstellen in C ++ Implementierung ist), zahlen Sie Basisklassen mal Zeigergröße in der Objektinstanz Größe. Der Anstieg der Speicherbedarf kann indirekte Auswirkungen auf die Leistung haben.

Mit abstrakten Basisklassen in C ++ Mandate im Allgemeinen die Verwendung einer virtuellen Funktionstabelle, die alle Ihre Schnittstelle Anrufe über diese Tabelle nachgeschlagen werden werden. Die Kosten sind winzig im Vergleich zu einem rohen Funktionsaufruf, so sicher sein, dass Sie müssen gehen schneller als vor darüber Sorgen.

Der einzige wesentliche Unterschied ich kenne, ist, dass, da Sie kein Beton-Klasse, inlining ist (viel?) Schwerer zu tun.

Das einzige, was ich denken kann, ist, dass virtuelle Methoden ein wenig langsamer zu nennen sind als nicht-virtuelle Methoden, da der Aufruf durch die virtuelle Methodentabelle .

Dies ist jedoch ein schlechter Grund, Ihr Design zu vermasseln. Wenn Sie mehr Leistung benötigen, einen schnelleren Server verwenden.

Wie für jede Klasse, die eine virtuelle Funktion enthält, wird eine V-Tabelle verwendet. Offensichtlich ein Verfahren durch einen Dispatching Mechanismus wie ein Vtable Aufruf ist langsamer als ein direkter Aufruf, aber in den meisten Fällen können Sie damit leben.

Ja, aber nichts bemerkenswert, mein Wissen. Die Leistungseinbußen ist wegen ‚indirection‘ Sie in jedem Methodenaufruf haben.

Doch es hängt wirklich von dem Compiler verwenden Sie da einige Compiler nicht in der Lage sind, die Methodenaufrufe innerhalb der Klassen Inline von der abstrakten Basisklasse erben.

Wenn Sie sicher sein wollen, sollten Sie Ihre eigenen Tests durchführen.

Ja, es ist eine Strafe. Etwas, was die Leistung auf Ihrer Plattform verbessern kann, ist eine nicht-abstrakte Klasse ohne virtuelle Funktionen zu nutzen. Dann mit einem Mitglied Funktionszeiger auf Ihre nicht-virtuelle Funktion.

Ich weiß, es ist eine ungewöhnliche Sicht, aber auch dieses Problem zu erwähnen, mache ich vermute, dass Sie setzen viel zu viele Gedanken in die Klassenstruktur. Ich habe viele Systeme gesehen, dass viel zu viele „Abstraktionsebene“ hatten, und das allein machte sie anfällig für schwere Performance-Probleme, nicht wegen der Kosten für die Methodenaufrufe, aber aufgrund der Tendenz unnötiger Anrufe zu tätigen. Wenn dies über mehrere Ebenen geschieht, ist es ein Killer. einen Blick nehmen

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top