C ++ Niedrige Latenz Design: Funktion Versand v / s CRTP für Factory-Implementierung

8

Als Teil eines Systemdesigns müssen wir ein Fabrikmuster implementieren. In Kombination mit dem Factory-Muster verwenden wir auch CRTP, um eine Basisfunktionalität bereitzustellen, die dann von den abgeleiteten Klassen angepasst werden kann.

Beispielcode unten:

%Vor%

Obwohl dieses Design gewunden ist, bietet es ein paar Vorteile. Alle Anrufe nach dem ersten virtuellen Funktionsaufruf können inline sein. Die abgeleitete Klasse custom_X_impl wird ebenfalls effizient erstellt.

Ich habe ein Vergleichsprogramm geschrieben, um das Verhalten für eine ähnliche Implementierung (Tight Loop, wiederholte Aufrufe) mit Funktionszeigern und virtuellen Funktionen zu vergleichen. Dieser Entwurf kam Triumphe für gcc / 4.8 mit O2 und O3.

Ein C ++ - Guru sagte mir jedoch gestern, dass jeder virtuelle Funktionsaufruf in einem großen ausführenden Programm eine variable Zeit in Anspruch nehmen kann, wenn man Cachespeicherfehlschläge berücksichtigt und ich eine bessere Leistung mit C-Style-Funktionstabellen-Lookups und GCC-Hotlisting erzielen kann Funktionen. Allerdings sehe ich immer noch 2x die Kosten in meinem oben genannten Beispielprogramm.

Meine Fragen sind wie folgt: 1. Ist die Behauptung des Gurus wahr? Für beide Antworten, gibt es irgendwelche Links, auf die ich verweisen kann. 2. Gibt es eine Implementierung mit niedriger Latenz, auf die ich verweisen kann, eine Basisklasse, die eine benutzerdefinierte Funktion in einer abgeleiteten Klasse mithilfe von Funktionszeigern aufruft? 3. Irgendwelche Vorschläge zur Verbesserung des Designs?

Jede andere Rückmeldung ist immer willkommen.

    
Sid 14.03.2015, 18:07
quelle

1 Antwort

2

Dein Guru bezieht sich auf das heiße Attribut des gcc-Compilers. Der Effekt dieses Attributs ist:

  

Die Funktion ist aggressiver optimiert und auf vielen Zielen ist sie   in einem speziellen Unterabschnitt des Textabschnitts platziert so alles heiß   Funktionen erscheinen nahe beieinander und verbessern die Lokalität.

Ja, in einer sehr großen Codebasis kann die Hotlist-Funktion im Cache verbleiben, damit sie ohne Verzögerung ausgeführt werden kann, weil sie den Cache nicht vermisst.

Sie können dieses Attribut perfekt für Memberfunktionen verwenden:

%Vor%

Aber ...

Wenn Sie virtuelle Funktionen verwenden, generiert der Compiler im Allgemeinen eine vtable geteilt zwischen allen Objekten der Klasse. Diese Tabelle ist eine Tabelle mit Zeigern auf Funktionen. Und in der Tat - Ihr Guru hat Recht - garantiert nichts, dass dieser Tisch im Cache gespeichert bleibt.

Wenn Sie jedoch manuell eine "C-style" Tabelle mit Funktionszeigern erstellen, ist das Problem GENAU GLEICH. Während die Funktion im Cache verbleibt, stellt nichts sicher, dass Ihre Funktionstabelle auch im Cache verbleibt.

Der Hauptunterschied zwischen den beiden Ansätzen ist folgender:

  • Im Falle virtueller Funktionen weiß der Compiler, dass die virtuelle Funktion ein Hotspot ist und könnte entscheiden, ob er die vtable auch im Cache behalten soll (ich weiß nicht, ob gcc das kann) oder wenn es Pläne dafür gibt).

  • Im Falle der Zeiger-Tabelle der manuellen Funktionen wird Ihr Compiler nicht leicht folgern, dass die Tabelle zu einem Hot-Spot gehört. Dieser Versuch der manuellen Optimierung könnte also sehr wahrscheinlich fehlschlagen.

Meine Meinung: Versuchen Sie nie, sich selbst zu optimieren, was ein Compiler viel besser machen kann.

Fazit

Vertrauen Sie auf Ihre Benchmarks. Und vertrauen Sie Ihrem Betriebssystem: Wenn Ihre Funktion oder Ihre Daten häufig bearbeitet werden, gibt es hohe Chancen, dass ein modernes Betriebssystem dies in seiner virtuellen Speicherverwaltung berücksichtigt und was auch immer der Compiler generiert.

    
Christophe 14.03.2015, 18:39
quelle