Pourquoi chaque octet compte en performance
Pourquoi ça compte pour toi
Si tu construis un système qui traite beaucoup de données (jeux, data science, simulations), l'arrangement de tes structures en mémoire fait la différence entre 3 nanosecondes et 163 nanosecondes par accès. C'est pas de la micro-optimisation de barbu : c'est de l'architecture qui impacte directement ta capacité à monter en charge et tes coûts cloud.
Ce qu'il faut retenir
- 1.Les processeurs chargent 64 octets à la fois : si tu n'utilises qu'1 octet, tu gaspilles 63 octets de cache
- 2.Passer d'une structure de 64 o à 128 o te bascule du cache L1 (3 ns) au cache L2 (11 ns) à seulement 512 éléments
- 3.L'arrangement en mémoire : Array of Structs (AoS) vs Struct of Arrays (SoA) peut donner 30x d'écart selon ton schéma d'accès
Tu galères avec le jargon ?
Lis la version réécrite en mode débutant — toutes les idées, sans le jargon.
Le hardware parle plus fort que l'algorithme
Depuis des années, on te dit que O(N) c'est O(N) : si ta boucle est linéaire, c'est linéaire. Faux.
Deux boucles apparemment identiques peuvent avoir des latences complètement différentes selon comment les données sont arrangées en mémoire et comment le CPU les charge.
Comment ça marche : les lignes de cache
Quand tu lis un octet, le CPU ne charge pas juste cet octet — il attrape 64 octets (ta ligne de cache) et les cale en cache L1. C'est une optimisation : les données qu'on accède sont généralement proches les unes des autres en mémoire.
Voilà les hiérarchies réelles :
- ▸Cache L1d : ~35 Kio/cœur, ~4-5 cycles, ~1-2 ns (35 Kio ÷ 64 o = ~560 lignes de cache)
- ▸Cache L2 : ~2 Mio/paire, ~12-15 cycles, ~4-5 ns
- ▸Cache L3 : 12 Mio partagé, ~30-40 cycles, ~10-15 ns
- ▸DRAM : mémoire principale, ~100-200 cycles, ~60-100 ns
Quand ta structure ne rentre plus en L1, tu tombes 4 à 10 fois plus lent. C'est violent.
Le cas concret : Array of Structs vs Struct of Arrays
Imagine une liste de 4096 monstres de jeu, chacun avec des stats (id, position xyz, vitesse xyz, hp, attaque, défense, vivant, équipe, nom).
Disposition classique (AoS) :
struct Monster {
uint32_t id; // 4 octets
float x, y, z; // 12 octets
// ... autres champs
uint8_t is_alive; // 1 octet ← c'est le seul qu'on veut
};
// Total : 64 octets
Quand tu itères pour filtrer les vivants, tu charges 64 octets entiers par monstre. La ligne de cache contient UNE SEULE structure.
Disposition optimisée (SoA) :
struct Monsters {
uint32_t *ids;
float *xs, *ys, *zs;
uint8_t *is_alives; // 4096 octets de suite
};
Là, une seule ligne de cache (64 octets) contient 64 statuts de monstres différents. Tu en charges 64 d'un coup.
Résultat ? Jusqu'à 30x plus rapide avec la bonne structure pour le bon schéma d'accès.
Accès aléatoire : quand la taille totale devient le goulot
C'est pire avec les graphes, tables de hachage, ou le pointer-chasing (vraiment aléatoire). Le CPU ne peut pas prédire où tu vas aller — il faut que tout soit en cache pour ne pas caler.
Regarde les latences réelles mesurées :
| Nombre de monstres | Empreinte 64 o | Latence 64 o | Empreinte 128 o | Latence 128 o |
|---|---|---|---|---|
| 512 | 32 Kio | ~3 ns | 64 Kio | ~11 ns |
| 4 096 | 256 Kio | ~11 ns | 512 Kio | ~13 ns |
| 32 768 | 2 Mio | ~29 ns | 4 Mio | ~43 ns |
| 131 072 | 8 Mio | ~163 ns | 16 Mio | ~162 ns |
Doubler de 64 o à 128 o, c'est doubler ton empreinte mémoire. À 512 éléments, tu passes du L1 (3 ns) au L2 (11 ns). À 32k, c'est +50% de latence.
Donc ?
Aujourd'hui, beaucoup de devs ignorent où vivent leurs données. En Java tu ajoutes un champ, tu t'en fous. Mais sur du traitement en masse (ML, gaming, data processing), c'est ton deuxième goulot après l'algorithme.
Avant d'ajouter un truc à ta structure :
- ▸Demande-toi si c'est vraiment nécessaire
- ▸Mesure ton empreinte totale avec
lscpu - ▸Si tu fais de l'accès aléatoire, compte les mégaoctets
- ▸Envisage SoA pour le chemin critique
Chaque octet compte.
Et concrètement pour toi ?
Choisis ton profil — la lecture de l'article change selon qui tu es.
Pour toi, retiens que chaque octet compte parce que les processeurs n'aiment pas attendre : la mémoire est le vrai goulot, pas la CPU. C'est pourquoi même l'IA et le cloud peuvent être bloqués par des choix de conception qu'on ne voit pas.
Source
Pour aller plus loin
Cet article t'a donné envie d'approfondir ? Deux formations Noésis t'attendent :