39 Punkte von GN⁺ 2025-04-21 | 1 Kommentare | Auf WhatsApp teilen
  • Ein Open-Source-Projekt, mit dem sich High-Performance-C/C++- und Assembler-Coding-Techniken anhand praxisnaher Beispiele lernen lassen
  • Enthält Beispiele für den Einsatz optimierter Bibliotheken statt STL sowie verschiedener Hardware-Optimierungstechniken
  • Erklärt zahlreiche Performance-Tricks wie Kosten der Eingabegenerierung, Approximation mathematischer Funktionen, CPU-Branch-Prediction und Multicore-Parallelisierung
  • Behandelt umfassend plattformbezogene Optimierungstechniken für CUDA, PTX, ASM, FPGA, JSON-Verarbeitung usw. sowie Methoden zur Benchmark-Messung
  • Bietet auf Basis von Google Benchmark Funktionen zur Automatisierung von Benchmark-Ausführung und statistischer Auswertung

So schreibt man performanceorientierten C/C++- und Assembler-Code

  • Dieses Projekt ist eine Sammlung von Benchmark-Code, die dabei hilft, die für das Design von Hochleistungssoftware nötige Intuition und Denkweise zu entwickeln
  • Es behandelt praxisnahe Codebeispiele, um typische Bugs, Sicherheitsprobleme und Performance-Engpässe in modernem Code zu vermeiden
  • Es stellt systematisch performanceorientierte Techniken aus der Praxis vor, die in Uni-Vorlesungen oder Bootcamps kaum vermittelt werden
  • Der Großteil des Codes läuft in Linux-Umgebungen auf Basis von GCC und Clang, teilweise werden aber auch Windows und macOS unterstützt
  • Vorgestellt werden außerdem parallele Algorithmen, Coroutines und Polymorphismus für die Implementierung performanter Software

Wichtige Themen

  • Zufällige Eingaben zum hundertfach niedrigeren Preis?! Die Eingabegenerierung kann langsamer sein als der Algorithmus selbst
  • 1 % Fehler bei nur 1/40 der Kosten: STL-Trigonometriefunktionen wie std::sin in nur drei Zeilen Code approximieren
  • Lazy-Logik ist viermal schneller? Extreme Faulheit mit benutzerdefinierten std::ranges und Iteratoren umsetzen
  • Compiler-Optimierung über -O3 hinaus: Mit versteckten Flags und Tricks lässt sich die Performance noch einmal verdoppeln
  • Matrizenmultiplikation ist das Problem? 3x3x3-GEMM kann trotz 60 % weniger Operationen 70 % langsamer sein als 4x4x4
  • Die Wahrheit über AI-Scaling? Die Lücke zwischen theoretischem ALU-Durchsatz und realer BLAS-Performance messen
  • Wie viele ifs sind zu viele? Mit nur zehn Zeilen Code die Grenzen des CPU-Branch-Predictors testen
  • Ist Rekursion wirklich besser? Die Stack-Tiefe selbst messen und sehen, wo SEGFAULT auftritt
  • Warum Ausnahmen vermieden werden sollten? Alternativen wie std::error_code oder std::variant ausprobieren
  • Wie skaliert man auf Multicore? Mit OpenMP, Intel oneTBB oder einem selbstgebauten Thread-Pool
  • Wie verarbeitet man JSON ohne Speicherallokation? Ist C++20 besser, oder sind klassische C99-Tools einfacher?
  • STL-assoziative Container richtig nutzen: Wie verwendet man benutzerdefinierte Schlüssel und transparente Vergleichsoperatoren?
  • Gibt es etwas Schnelleres als einen handgeschriebenen Parser? Direktvergleich mit einer auf consteval basierenden Regex-Engine
  • Sind Pointer wirklich 64 Bit groß? Pointer-Tagging in der Praxis nutzen
  • Wie viele Pakete verwirft UDP? Bis hin zur Verarbeitung von Web-Requests aus dem User Space mit io_uring
  • Scatter-Gather für 50 % schnellere vektorisierte Operationen auf nicht zusammenhängendem Speicher
  • Intel oneAPI vs. Nvidia CCCL? Was ist besonders an <thrust> und <cub>?
  • CUDA C++, PTX, SASS: Worin unterscheiden sie sich von CPU-Code?
  • Wenn der Code performancekritisch ist: Vergleich von Intrinsics, Inline-asm und .S-Dateien
  • Tensor Cores und Speicherarchitektur — wie unterscheiden sich CPU sowie Volta-, Ampere-, Hopper- und Blackwell-GPUs?
  • Wie unterscheidet sich FPGA-Coding von GPU-Programmierung? Was sind die Unterschiede zwischen High-Level Synthesis (HLS), Verilog und VHDL? 🔜 #36
  • Was ist eine Encrypted Enclave? Latenzvergleich von Intel SGX, AMD SEV und ARM Realm 🔜 #31

Ausführung und Einrichtung

  • Linux + GCC empfohlen, auch WSL oder Clang auf dem Mac (nicht die Standard-Distribution) sind nutzbar
  • Erforderlich sind CMake, liburing, OpenBLAS, g++, build-essential
  • Nach dem Build der ausführbaren Datei less_slow werden die Benchmarks beim Start automatisch ausgeführt
git clone https://github.com/ashvardanian/less_slow.cpp.git  
cd less_slow.cpp  
pip install cmake --upgrade  
sudo apt install -y build-essential g++ liburing-dev libopenblas-base  
cmake -B build_release -D CMAKE_BUILD_TYPE=Release  
cmake --build build_release --config Release  
build_release/less_slow  
  • Ob CUDA und Intel TBB verwendet werden, ist optional (-D USE_INTEL_TBB=OFF usw.)
  • Beim Ausführen lassen sich einzelne Benchmarks auswählen, Ergebnisse als JSON speichern und Ausgabeformate festlegen
build_release/less_slow --benchmark_filter=std_sort  
build_release/less_slow --benchmark_out=results.json --benchmark_format=json  

Tipps für bessere Performance-Messungen

  • SMT deaktivieren und random interleaving verwenden, um Rauschen zu minimieren
  • Mit der Option --benchmark_perf_counters von Google Benchmark lassen sich Hardware-Performance-Counter messen
sudo build_release/less_slow --benchmark_perf_counters="CYCLES,INSTRUCTIONS"  
  • Alternativ kann der Benchmark auch mit dem Linux-Tool perf gemessen werden
sudo perf stat taskset 0xEFFFEFFFEFFFEFFFEFFFEFFFEFFFEFFF build_release/less_slow --benchmark_filter=super_sort  

Projekt-Dateistruktur

  • Hauptquelle: less_slow.cpp (Schwerpunkt auf CPU-Benchmark-Code)
  • Enthält plattformspezifische Optimierungsdateien: ASM für x86/ARM, CUDA-.cu und PTX-.ptx-Code
├── less_slow.cpp           # Haupt-Benchmark-Code  
├── less_slow_amd64.S       # x86-Assembler  
├── less_slow_aarch64.S     # ARM-Assembler  
├── less_slow.cu            # CUDA C++  
├── less_slow_sm70.ptx      # PTX IR (Volta)  
├── less_slow_sm90a.ptx     # PTX IR (Hopper)  

Verwendung externer Bibliotheken

  • Google Benchmark: Performance-Messung
  • Intel oneTBB: Paralleles STL-Backend
  • Meta libunifex: Asynchrones Ausführungsmodell
  • range-v3: Ersatz für std::ranges
  • fmt: Ersatz für std::format
  • StringZilla: Ersatz für std::string
  • CTRE: Ersatz für std::regex
  • nlohmann/json, yyjson: JSON-Parser
  • Abseil: High-Performance-Container
  • cppcoro: Coroutine-Implementierung
  • liburing: Linux-Kernel-umgehendes I/O
  • ASIO: Asynchrones Networking
  • Nvidia CCCL, CUTLASS: GPU-Algorithmen und Matrixoperationen

Kurzüberblick: Tipps zur Nutzung von Google Benchmark

  • Benchmarks mit BENCHMARK() registrieren, Parameter mit ->Args({x,y}) übergeben
  • Compiler-Optimierung mit DoNotOptimize() und ClobberMemory() steuern
  • Anzahl der Wiederholungen und Benchmark-Dauer mit ->Iterations(n) und ->MinTime(n) festlegen
  • Zeitkomplexität mit ->Complexity(...) und ->SetComplexityN(n) angeben
  • Timing-Bereiche mit state.PauseTiming() und ResumeTiming() direkt steuern
  • Benutzerdefinierte Counter lassen sich über state.counters[...] registrieren

Meme- und Humor-Elemente

  • In das Lehrmaterial sind technische Meme-Bilder eingebaut, um das Interesse zu steigern
  • Der Konflikt zwischen Performance und Abstraktion sowie IEEE-754-Gleitkommazahlen werden humorvoll dargestellt

1 Kommentare

 
GN⁺ 2025-04-21
Hacker-News-Kommentare
  • 40-mal schnellere Trigonometrie: Standardbibliotheksfunktionen wie std::sin lassen sich mit 3 Zeilen Code beschleunigen

    • Man kann sin(x) annähern, indem man die Entwicklung auf einige wenige Terme begrenzt
    • Der Rechenaufwand sinkt, aber auch die Genauigkeit nimmt ab
    • Der Genauigkeitsverlust wird noch unterschätzt. Für Eingaben außerhalb des Bereichs [-2, 2] ist das Ergebnis extrem ungenau
    • Es kann nicht einmal ein einzelnes Sinuswellenintervall korrekt behandeln und berücksichtigt die periodischen Eigenschaften nicht. Eine nutzlose „Optimierung“
  • Erfahrung aus der Arbeit mit Mikrocontrollern

    • Arbeit an Embedded-Systemen, mit etwa 256 KiB Heap und einem größten Stack von 4 KiB
    • Es wird überwiegend modernes C++ verwendet, aber nicht jeder Trick passt zu jeder Situation
    • CTRE ist in Ordnung, solange kein Stack Overflow auftritt. Beim Versuch, Strings in einer HTTP-Proxy-Konfiguration zu validieren, ist das System wegen eines Stack Overflow abgestürzt
    • JSON wird intern kaum verwendet; stattdessen wurde eine eigene BSON-Bibliothek geschrieben. Dadurch muss man sich keine Gedanken über Speicherallokation oder Fragmentierung machen
    • Statt newlib wird picolibc verwendet, und der Locale-Code der C/C++-Standardbibliothek wurde entfernt. Das reduziert die Programmgröße
  • Meinung zu den Entscheidungen von Abseil

    • Als es erstmals erschien, war es ein großes Thema, aber inzwischen gibt es mehrere Alternativen, die seine Schwächen verbessert haben
    • In den letzten Jahren haben die Beschwerden über Abseil zugenommen. Bei Google sind zentrale Maintainer der Kernbibliotheken weggegangen
  • Kritik an Verrenkungen für Performance in C++

    • Überraschend, dass CTRE gute Ergebnisse liefert. Man müsste tiefer einsteigen
    • Es besteht Interesse daran, OpenMP- und TBB-Threadpool-Benchmarks zu untersuchen und zu prüfen, ob sich auch ein Boost::ASIO-Threadpool hinzufügen lässt
  • Unterschiede zwischen FPGA- und GPU-Programmierung sowie der Wunsch nach Inhalten zu High-Level-Synthese, Verilog und VHDL

    • Dazu wären bevorzugt weitere Anfragen willkommen
  • Neue Informationen über denormalisierte Gleitkommazahlen

    • Beim Multiplizieren von Matrizen auf GPUs fragt man sich das gelegentlich
  • Positives Feedback zum Beitrag über Google Benchmark

    • Der Fokus auf Performance-Benchmarking ist gut. Das Repository ist gut organisiert
  • Erwartungen an „weniger langsamen C-, C++- und Assembler-Code“

    • Es wurde erwartet, dass auch C-Code enthalten ist, aber vorhanden sind nur .cpp und .S
    • less_slow.cpp verwendet viele C++-Eigenschaften. Entweder sollte „C“ aus der Liste entfernt werden oder es braucht eine Korrektur