Statische Vererbung via CRTP - wie funktioniert es genau?



  • Lass den Unsinn und mach das normal mit normaler Vererbung und virtuellen Methoden. Wenn es dir um Performance geht, markiere die Methoden als final. Damit kann der Compiler in fast allen Situationen devirtualisieren.



  • asy schrieb:

    Lass den Unsinn und mach das normal mit normaler Vererbung und virtuellen Methoden. Wenn es dir um Performance geht, markiere die Methoden als final. Damit kann der Compiler in fast allen Situationen devirtualisieren.

    Danke für den Hinweis. Leider hatte ich das schon getan. Mit MSVC 2013 brachte "final" aber keine Verbesserung. Da ich mit Qt arbeite, bin ich noch auf MSVC2013 festgelegt. Kann MSVC2015 besser devirtualisieren?



  • Mr.Long schrieb:

    Du hast hier CRTP verwenden um weniger schreibaufwand zu haben, das war's aber auch schon wieder...Wenn es wirklich um hochkritischen Grafikoperationen geht, musst du dir überlegen ob du dein Design ändern willst um die dynamische Bindung rauszuschmeißen.

    Meine erste Programmversion verwendet einfach "switch" Schalter und ein Typ Flag um zwischen den verschiedenen Figuren in der Methode "Zeichne" zu unterscheiden.

    Als ich das Programm dann mit einfacher Vererbung umschrieb, hatte ich bei den Grafikanimationen einen Performance Verlust in der Debug Version von 25% und im Release von 33%. Zudem liefen die Grafikrotation nicht mehr wie zuvor in gleichmäßiger Geschwindigkeit, sondern ruckelten leicht.

    Nach einer Recherche im Netz las ich dann von CRTP als möglichen Ersatz für Vererbung bei zeitkritischen Anwendungsteilen. Nun sagst Du, dass CRTP dafür nicht geeignet ist und es besser ist ein anderes Design zu verwenden.

    Sollte ich es dann bei der "switch / flag" Methode belassen. Die sieht nicht sehr elegant aus, ist aber sehr schnell und flüssig. Oder gibt es noch bessere Design Methoden für zeitkritische Programmteile?



  • Profile dein Programm und vergleiche mit der Methode vorher. Ich bezweifle dass es an der Vererbung liegt.



  • Ich arbeite mit Qt und dem Qt Creator unter Windows mit dem MSVC2013 Compiler. Da gibt es für den Qt Creator keinen Profiler.

    Bin mir auch nicht sicher, ob ein Profiler weiterhelfen würde. Denn ich habe einfach nur eine "switch" Anweisung mit 10 Einsprüngen durch eine Basisklasse mit 10 Ableitungen ersetzt. Die Funktion "Zeichne", die die verschiedenen Grafiken zeichnet, ist unverändert geblieben. Viel zu analysieren gibt es da nicht.

    Mein Beispiel hier mit Rechteck und Quadrat ist nur ein Minimalbeispiel. Die jeweiligen "Figuren" sind im Programm wesentlich komplexer.



  • mireiner schrieb:

    Meine erste Programmversion verwendet einfach "switch" Schalter und ein Typ Flag um zwischen den verschiedenen Figuren in der Methode "Zeichne" zu unterscheiden.

    Als ich das Programm dann mit einfacher Vererbung umschrieb, hatte ich bei den Grafikanimationen einen Performance Verlust in der Debug Version von 25% und im Release von 33%. Zudem liefen die Grafikrotation nicht mehr wie zuvor in gleichmäßiger Geschwindigkeit, sondern ruckelten leicht.

    Genau das Gegenteil von dem was du gemacht hast machen Compiler, wenn sie es können, als Optimierung. Nennt sich "devirtualization".
    Also nicht wirklich verwunderlich dass es mit dem virtuellen Funktionsaufruf langsamer ist.
    Erstmal ist so ein virtueller Funktionsaufruf auch nicht GANZ billig, und vor allem verhindert er Inlining. Und durch das verhinderte Inlining oft noch weitere Optimierungen im "umgebenden" Code.

    mireiner schrieb:

    Nach einer Recherche im Netz las ich dann von CRTP als möglichen Ersatz für Vererbung bei zeitkritischen Anwendungsteilen. Nun sagst Du, dass CRTP dafür nicht geeignet ist und es besser ist ein anderes Design zu verwenden.

    Sollte ich es dann bei der "switch / flag" Methode belassen. Die sieht nicht sehr elegant aus, ist aber sehr schnell und flüssig. Oder gibt es noch bessere Design Methoden für zeitkritische Programmteile?

    Am meisten würde hier vermutlich bringen die Entscheidung um was für eine "Form" es sich handelt aus der innersten Schleife rauszuziehen.
    Also statt

    for each thing
        switch (thing.kind)
            ...
    

    besser

    for each kind
        for each thing of that kind
            ...
    

    Wobei die "for each thing of that kind" Schleife natürlich nicht so implementiert sein darf dass man über alle Elemente drüberiteriert und die unpassenden überspringt. Wäre ja sonst wieder langsam. Heisst: du brauchst dazu eine Datenstruktur wo es ohne Overhead möglich ist nur über Elemente eines bestimmten Typs zu iterieren.
    Also z.B. ne multi_map<kind, thing> oder, vermutlich besser, ne map<kind, vector<thing>> .

    In der äusseren Schleife kann man dann auch wieder problemlos virtuelle Funktionen aufrufen, weil sie selten genug aufgerufen werden.



  • Ich habe mich jetzt dazu entschieden in den zeitkritischen Grafikteilen bei der "switch" Methode zu bleiben, d.h. dort keine virtuellen Funktionen zu verwenden. Weil die "switch" Methode schnell in meinem Programm ist und einwandfrei funktioniert. Nur werde ich sie noch ein wenig umschreiben, um sie eleganter zu gestalten.



  • roflo schrieb:

    ...Ich bezweifle dass es an der Vererbung liegt.

    Du hattest recht. Mir ließ es keine Ruhe, dass die Grafikrotationen allein durch Vererbung so langsam und ruckelig geworden waren. Deshalb habe ich die kritischen Programmteile mit einfacher Vererbung nochmals umgeschrieben. Nun läuft die "switch" Version nur noch ca. 5% schneller und die Grafikrotationen minimal flüssiger. Die zuvor berichteten 33% Unterschied zu Lasten der Vererbung gingen auf mein Konto, aufgrund schlechter Architektur. Warum die Version mit Vererbung minimal Ruckelt (weniger flüssig läuft) bliebt ein Rätsel.



  • mireiner schrieb:

    Ich habe mich jetzt dazu entschieden in den zeitkritischen Grafikteilen bei der "switch" Methode zu bleiben, d.h. dort keine virtuellen Funktionen zu verwenden.

    löl



  • Tja. Zwei (oder mehr) Änderungen auf einmal zu testen und dann davon ausgehen zu wissen welche einen bestimmten Effekt verursacht hat ... da kann man sich schnell täuschen.


Anmelden zum Antworten