39 points par GN⁺ 2025-04-21 | 1 commentaires | Partager sur WhatsApp
  • Projet open source permettant d’apprendre des techniques de codage C/C++ et assembleur haute performance à travers des exemples concrets
  • Inclut des exemples d’utilisation de bibliothèques optimisées à la place de la STL, ainsi que diverses techniques d’optimisation matérielle
  • Explique différents trucs de performance comme le coût de génération des entrées, l’approximation de fonctions mathématiques, la prédiction de branchement CPU ou la parallélisation multicœur
  • Couvre largement les techniques d’optimisation selon les plateformes ainsi que les méthodes de mesure de benchmark, avec CUDA, PTX, ASM, FPGA, traitement JSON, etc.
  • Fournit une fonction d’automatisation de l’exécution des benchmarks et du traitement statistique basée sur Google Benchmark

Écrire du code C/C++ et assembleur orienté performance

  • Ce projet est une collection de codes de benchmark conçue pour aider à développer l’intuition et l’état d’esprit nécessaires à la conception de logiciels haute performance
  • Il présente des exemples pratiques de codage pour éviter les bugs, problèmes de sécurité et goulets d’étranglement de performance fréquents dans le code moderne
  • Il introduit de manière structurée des techniques orientées performance difficiles à rencontrer dans des cours universitaires ou bootcamps
  • La majorité du code fonctionne sous Linux avec GCC et Clang, avec une prise en charge partielle de Windows et macOS
  • Il présente aussi des éléments comme les algorithmes parallèles, les coroutines et le polymorphisme pour implémenter du code haute performance

Principaux sujets

  • Des entrées aléatoires 100 fois moins chères ?! Quand générer les entrées peut être plus lent que l’algorithme lui-même
  • 1 % d’erreur pour un coût divisé par 40 : approximer des fonctions trigonométriques STL comme std::sin en seulement 3 lignes de code
  • Une logique paresseuse 4 fois plus rapide ? Pousser la paresse à l’extrême avec des std::ranges personnalisés et des itérateurs
  • Une optimisation compilateur au-delà de -O3 : des flags cachés et des astuces peuvent encore doubler les performances
  • Le vrai problème, c’est la multiplication de matrices ? Avec 60 % d’opérations en moins, un GEMM 3x3x3 peut pourtant être 70 % plus lent qu’un 4x4x4
  • La vérité sur le scaling de l’IA ? Mesurer l’écart entre le débit théorique de l’ALU et les performances réelles de BLAS
  • À partir de combien de if y en a-t-il trop ? Tester les limites du prédicteur de branchement CPU en seulement 10 lignes de code
  • La récursion est meilleure ? Mesurons directement la profondeur de pile à laquelle survient un SEGFAULT
  • Pourquoi faut-il éviter les exceptions ? Et si on utilisait à la place std::error_code ou std::variant ?
  • Comment passer à l’échelle sur plusieurs cœurs ? Avec OpenMP, Intel oneTBB, ou même un pool de threads maison
  • Comment traiter du JSON sans allocation mémoire ? C++20 est-il meilleur, ou les outils plus anciens en C99 sont-ils plus simples ?
  • Pour bien utiliser les conteneurs associatifs de la STL, comment exploiter des clés personnalisées et des comparateurs transparents ?
  • Et s’il existait plus rapide qu’un parseur fait main ? Confrontation directe avec un moteur d’expressions régulières basé sur consteval
  • La taille d’un pointeur fait-elle vraiment 64 bits ? Tirons parti du tagging de pointeurs
  • Combien de paquets l’UDP perd-il réellement ? Jusqu’au traitement de requêtes web en espace utilisateur avec io_uring
  • Implémenter des opérations vectorisées sur mémoire non contiguë 50 % plus rapides avec le scatter-gather
  • Intel oneAPI vs Nvidia CCCL ? Qu’ont donc de spécial <thrust> et <cub> ?
  • CUDA C++, PTX, SASS : en quoi cela diffère-t-il du code CPU ?
  • Pour du code sensible aux performances ? Comparaison entre intrinsics, asm inline et fichiers .S pour faire le bon choix
  • Tensor Cores et structure mémoire — quelles différences entre CPU et GPU Volta, Ampere, Hopper ou Blackwell ?
  • En quoi coder pour FPGA diffère-t-il d’un GPU ? Quelles différences entre synthèse de haut niveau (HLS), Verilog et VHDL ? 🔜 #36
  • Qu’est-ce qu’une enclave chiffrée ? Comparaison des latences entre Intel SGX, AMD SEV et ARM Realm 🔜 #31

Exécution et configuration de l’environnement

  • Environnement Linux + GCC recommandé, avec possibilité d’utiliser WSL ou Clang sur Mac (distribution non par défaut)
  • Installation requise de CMake, liburing, OpenBLAS, g++, build-essential
  • Après compilation de l’exécutable less_slow, son lancement exécute automatiquement les benchmarks
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  
  • Possibilité de choisir d’utiliser ou non CUDA et Intel TBB (avec des flags comme -D USE_INTEL_TBB=OFF)
  • À l’exécution, il est possible de ne lancer que certains benchmarks, d’enregistrer en JSON ou de choisir le format de sortie
build_release/less_slow --benchmark_filter=std_sort  
build_release/less_slow --benchmark_out=results.json --benchmark_format=json  

Conseils pour améliorer la mesure des performances

  • Désactiver le SMT et utiliser le random interleaving pour minimiser le bruit
  • L’option --benchmark_perf_counters de Google Benchmark permet de mesurer les compteurs matériels de performance
sudo build_release/less_slow --benchmark_perf_counters="CYCLES,INSTRUCTIONS"  
  • Il est aussi possible de mesurer les benchmarks avec l’outil Linux perf
sudo perf stat taskset 0xEFFFEFFFEFFFEFFFEFFFEFFFEFFFEFFF build_release/less_slow --benchmark_filter=super_sort  

Structure des fichiers du projet

  • Source principale : less_slow.cpp (centrée sur le code de benchmark CPU)
  • Comprend aussi des fichiers d’optimisation selon les plateformes : assembleur x86/ARM, code CUDA .cu, code PTX .ptx
├── less_slow.cpp           # Code principal de benchmark  
├── less_slow_amd64.S       # Assembleur x86  
├── less_slow_aarch64.S     # Assembleur ARM  
├── less_slow.cu            # CUDA C++  
├── less_slow_sm70.ptx      # PTX IR (Volta)  
├── less_slow_sm90a.ptx     # PTX IR (Hopper)  

Utilisation de bibliothèques externes

  • Google Benchmark : mesure des performances
  • Intel oneTBB : backend STL parallèle
  • Meta libunifex : modèle d’exécution asynchrone
  • range-v3 : alternative à std::ranges
  • fmt : alternative à std::format
  • StringZilla : alternative à std::string
  • CTRE : alternative à std::regex
  • nlohmann/json, yyjson : parseurs JSON
  • Abseil : conteneurs haute performance
  • cppcoro : implémentation de coroutines
  • liburing : E/S Linux contournant le noyau
  • ASIO : réseau asynchrone
  • Nvidia CCCL, CUTLASS : algorithmes GPU et calcul matriciel

Résumé des astuces d’utilisation de Google Benchmark

  • Enregistrer un benchmark avec BENCHMARK(), passer des paramètres avec ->Args({x,y})
  • Contrôler les optimisations du compilateur avec DoNotOptimize() et ClobberMemory()
  • Contrôler le nombre d’itérations et la durée avec ->Iterations(n) et ->MinTime(n)
  • Définir la complexité temporelle avec ->Complexity(...) et ->SetComplexityN(n)
  • Contrôler directement la zone chronométrée avec state.PauseTiming() et ResumeTiming()
  • Possibilité d’enregistrer des compteurs personnalisés avec state.counters[...]

Mèmes et éléments humoristiques

  • Insertion d’images de mèmes techniques dans le contenu pédagogique pour susciter l’intérêt
  • Opposition entre performance et abstraction, norme IEEE 754 sur les nombres à virgule flottante, etc., présentées avec humour

1 commentaires

 
GN⁺ 2025-04-21
Commentaires sur Hacker News
  • Trigonométrie 40 fois plus rapide : on peut accélérer des fonctions de la bibliothèque standard comme std::sin avec 3 lignes de code

    • On peut approximer sin(x) en limitant le développement à quelques termes
    • Le coût de calcul diminue, mais la précision baisse aussi
    • La perte de précision est sous-estimée. C'est très imprécis pour des entrées en dehors de l'intervalle [-2, 2]
    • Cela ne peut même pas gérer un seul intervalle de sinus et ne traite pas sa nature périodique. C'est une « optimisation » inutile
  • Partage d'expérience sur microcontrôleur

    • Travail sur des systèmes embarqués, avec un heap d'environ 256 KiB et la plus grande pile à 4 KiB
    • Utilise surtout du C++ moderne, mais toutes les astuces ne conviennent pas à toutes les situations
    • CTRE est acceptable tant qu'on évite le débordement de pile. Une tentative de validation d'une chaîne de configuration de proxy HTTP a fait planter le système à cause d'un stack overflow
    • N'utilise presque jamais JSON en interne et a écrit sa propre bibliothèque BSON. Pas besoin de se soucier des allocations mémoire ni de la fragmentation
    • Utilise picolibc au lieu de newlib et retire le code de locale de la bibliothèque standard C/C++. Cela réduit la taille du programme
  • Avis sur les choix d'Abseil

    • C'était un sujet majeur à son arrivée, mais il existe désormais plusieurs alternatives qui corrigent ses faiblesses
    • Le mécontentement envers Abseil a augmenté ces dernières années. Des mainteneurs de bibliothèques clés sont partis de Google
  • Critique des contorsions pour la performance en C++

    • Surprise que CTRE donne de bons résultats. Il faudrait creuser davantage
    • Envie d'examiner les benchmarks des pools de threads OpenMP et TBB, et de voir s'il est possible d'ajouter le pool de threads de Boost::ASIO
  • Différences entre le développement FPGA et GPU, et demande de contenu sur la synthèse avancée, Verilog et VHDL

    • Souhaite voir ce sujet traité en priorité
  • Nouvelle information sur les nombres à virgule flottante dénormalisés

    • Se pose parfois la question lors de multiplications de matrices sur GPU
  • Retour positif sur le billet consacré à Google Benchmark

    • Bonne focalisation sur le benchmarking des performances. Le dépôt est bien organisé
  • Attentes autour de « code C, C++ et assembleur moins lent »

    • S'attendait à voir aussi du code C, mais il n'y a que des fichiers .cpp et .S
    • less_slow.cpp utilise beaucoup de fonctionnalités C++. Il faut retirer « C » de la liste ou corriger cela