Performance Aware Programming

Entwickelt ihr "Performance-Aware"?

  • Ja, ich nutzte Cachefreundliche Datenstrukten und nutze die CPU effizient aus

    Stimmen: 7 25,0%
  • Ja ist mir bekannt, kann ich aber aus bestimmten Gründen nicht umsetzen

    Stimmen: 11 39,3%
  • Ja ist mir bekannt, aber ich verstehe es nicht

    Stimmen: 2 7,1%
  • Nein, klingt aber interessant und würde gerne mehr darüber erfahren

    Stimmen: 2 7,1%
  • Nein, denn Performance spielt keine Rolle in meinen Codes

    Stimmen: 6 21,4%

  • Umfrageteilnehmer
    28
  • Umfrage geschlossen .
kuddlmuddl schrieb:
Auf meinem GamingPC und aufm Laptop war kein Unterschied messbar. Dh klassisches OOP mit >1MB fetten Objekten vs 'alles relevante in einem seperatem std::vector' war exakt gleich schnell. Egal ob -march-native, -O2, -O3, -Os usw.
code zufällig noch da zum ansehen?
 
kuddlmuddl schrieb:
Ich hab nun schon 2x diesen Talk gesehen
und daher auch mal probiert "data oriented design", wie es hier heißt, in C++ zu benchmarken.
Auf meinem GamingPC und aufm Laptop war kein Unterschied messbar. Dh klassisches OOP mit >1MB fetten Objekten vs 'alles relevante in einem seperatem std::vector' war exakt gleich schnell. Egal ob -march-native, -O2, -O3, -Os usw.
Wahrscheinlich hat die https://de.wikipedia.org/wiki/Memory_Management_Unit und prefetching einfach perfekt funktioniert.

Ich halte es daher genau wie auch auf stackoverflow immer steht: Messen, messen, messen.
Nur weil man denkt etwas wäre langsam oder weil man denkt, "anders als sauber programmiert ist bestimmt schneller!!!11" sollte man es nicht ohne Grund tun. Und einen Grund liefert nur eine representative Messung.
Ich kenn diesen Talk und ich mag ihn sehr, weil er mir ziemlich stark aus der Seele gesprochen hat.

Ich habe damals das versucht nachzuvollziehen und habe ein komplexeres Projekt geschrieben, welches die Unterschiede zeigen soll - in 4 verschiedenen Programmierstilen.

Allerdings ist es nicht richtig umgesetzt, denn Version 1 und 2 ist nicht abstrakt genug und verletzt die S.O.L.I.D Prinzipien. Dazu ist Version 4 nicht Datenorientiert genug :-(

NBody SPH Simulation

Heraus kam dieses Projekt hier: https://github.com/f1nalspace/nbodysimulation_experiment

Eine Open-Source 2D SPH Fluid Simulation inkl. Implementierungen, bei der die letzte Datenorientiert sein sollte. Es gibt definitiv Unterschiede zwischen den Stilen, allerdings habe ich es wohl "zu gut" gemacht und selbst die langsamste Implementierung (I dont care) ist nur leicht langsamer als die letzte - was ich darauf zurückführe, dass viele Codes identisch sind (Parallelisierung, Berechnungen). Von SIMD hatte ich zu dem Zeitpunkt keinen Plan, deshalb ist es wirklich so naive wie nur möglich umgesetzt. Wer mag kann das gern mal clonen, bauen und laufen lassen. Es hat sogar einen eigenen Benchmark eingebaut mit anschließendem Bar-Chart.

Allerdings gibt es eine aktuellere Version davon mit Bugfixes & Improvements, welche in meinem FPL-Projekt als Sample dient, siehe: https://github.com/f1nalspace/final_game_tech/tree/develop/demos/FPL_NBodySimulation
 

Anhänge

  • nbodysim_1_4_3_7950x.jpg
    nbodysim_1_4_3_7950x.jpg
    268,5 KB · Aufrufe: 100
Zuletzt bearbeitet:
  • Gefällt mir
Reaktionen: Piktogramm und BeBur
KitKat::new() schrieb:
Hier ein Beispiel wie so mancher Codestyle auf wirklich große Performance Unterschiede hinausläuft: https://news.ycombinator.com/item?id=34966137#34974790
Wie hier im Thread diskutiert, kritisierte der Vortrag “Clean” code, horrible performance" u.a. die Performance bei Polymorphie (in C++). Ich war damals überrascht, daß auch C# und Java davon betroffen sind.
Allerdings erlaubt die Markierung einer Klasse als notInheritable/sealed/final dem Compiler Optimierungen vorzunehmen, die den Leistungsrückgang gegenüber dem switch Ansatz beinah aufheben. Ich vergesse die Markierung leider zu oft, aber vielleicht hilft ja dieses Niederschreiben nicht nur mir.

In Zahlen: der Benchmark für
Code:
public class Animal
{
    public virtual void DoNothing() { }
}

public class Bear : Animal
{
    public override void DoNothing() { }
}

public sealed class Husky : Animal
{
    public override void DoNothing() { }
}

liefert:
Code:
|            Method |      Mean |     Error |    StdDev |    Median |
|------------------ |----------:|----------:|----------:|----------:|
| Sealed VoidMethod | 0.0030 ns | 0.0056 ns | 0.0159 ns | 0.0000 ns |
|   Open VoidMethod | 0.6350 ns | 0.0661 ns | 0.1917 ns | 0.6182 ns |
[Quelle]
Ein Einwand könnte sein, daß die Verwendung von AbleitungsBlockern negative Auswirkungen auf das Testing hat. Ja, aber m.M.n. keine unlösbaren.
 
Zuletzt bearbeitet:
Ja das hilft für Funktionsaufrufe, damit werden die optimiert sowie lese/schreib Zugriffe auf Properties.

Allerdings Cache-Freundlich werden dadurch die Datenstrukturen natürlich magischerweise nicht.
Dies muss immernoch selbst gemacht werden, da der Compiler den Context einfach nicht kennt.
 
Finalspace schrieb:
Die Rede ist dabei aber nicht von Hardcore-Optimierungen, wie man es früher gemacht hat um jedes Bit zu sparen - sondern wirklich nur, wie löse ich mein Problem effizient, mit wenig Code und mit so wenig Balast wie nur möglich.
Wenn ich das nicht machen würde, wäre ich in meinem Beruf falsch.

Aber die Frage ist, glaube ich, etwas falsch gestellt ... meintest du nicht eher, wer kann noch ressourcenschonend programmieren?
 
Finalspace schrieb:
[...]
Eine Open-Source 2D SPH Fluid Simulation inkl. Implementierungen, bei der die letzte Datenorientiert sein sollte. Es gibt definitiv Unterschiede zwischen den Stilen, allerdings habe ich es wohl "zu gut" gemacht und selbst die langsamste Implementierung (I dont care) ist nur leicht langsamer als die letzte - was ich darauf zurückführe, dass viele Codes identisch sind (Parallelisierung, Berechnungen). [...]
Hatte mir vorgenommen den Code anzuschauen, hat zeitlich nicht gepasst und entsprechend spät kommt die Antwort.

Im Blick auf Objektorientierte Programmierung, selbst dein Worst Case hat ja nur wenig Aufrufe verschiedener Objekte. Um die immensen Performancedifferenzen zu erreichen, die in den Beispielen gegen Clean Code gezeigt werden, hättest du jedes Knoten/Datum in ein eigenes Objekt packen müssen. Dann wäre der Overhead für Initialisierung und Aufruf der Objekte ein Vielfaches höher im Vergleich zur eigentlichen Berechnung.
Das wäre absolut lächerliches Softwaredesign, CC fordert das auch an keiner Stelle, aber darauf stützt sich die Argumentation gegen CC.

Insofern überrascht mich dein Ergebnis kaum und ich würde gar vermuten, dass wenn die Aufgaben für die Benchmarks größer werden, die Differenz noch weiter schrumpft.
 
Zurück
Oben