- 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
Commentaires sur Hacker News
Trigonométrie 40 fois plus rapide : on peut accélérer des fonctions de la bibliothèque standard comme
std::sinavec 3 lignes de codesin(x)en limitant le développement à quelques termesPartage d'expérience sur microcontrôleur
Avis sur les choix d'Abseil
Critique des contorsions pour la performance en C++
Différences entre le développement FPGA et GPU, et demande de contenu sur la synthèse avancée, Verilog et VHDL
Nouvelle information sur les nombres à virgule flottante dénormalisés
Retour positif sur le billet consacré à Google Benchmark
Attentes autour de « code C, C++ et assembleur moins lent »