Linux 7.4 refait le plein des sheaves du slab depuis la grange et gagne 24 % en allocation mémoire
Une rustine de 81 lignes signée Hao Li, entrée dans slab/for-next avant la fenêtre de fusion de Linux 7.4, autorise le préremplissage des sheaves depuis la grange plutôt que depuis les slabs partiels, levant une saturation qui pénalisait kfree_rcu(). Résultat mesuré : +24 % sur le test will-it-scale mmap1 et des gains massifs sur les microbenchmarks du barn.
26 septembre 2026. Michael Larabel documente sur Phoronix une rustine de gestion mémoire entrée dans la branche slab/for-next, en amont de la fenêtre de fusion de Linux 7.4 prévue fin octobre. 26 septembre 2026. Son auteur, Hao Li, décrit un changement de 81 lignes qui modifie la façon dont l’allocateur slab recharge ses sheaves. Un an plus tôt, les sheaves faisaient leur entrée dans le noyau comme couche de cache par CPU. Pourquoi c’est important : une saturation discrète de la « grange » de l’allocateur forçait des opérations coûteuses à chaque kfree_rcu(), et cette rustine la lève — avec un gain mesuré de 24 % sur un benchmark réaliste.
Les sheaves, la couche de cache par CPU de l’allocateur
Les sheaves sont la couche de mise en cache par CPU, organisée en tableaux, de l’allocateur mémoire du noyau. Elles stockent des objets libérés pour les réallouer vite, sans repasser par les structures globales de l’allocateur SLUB. Depuis leur introduction l’an dernier, elles ont concentré une grande partie du travail d’optimisation de l’allocateur, parce qu’elles conditionnent directement la localité de cache et la contention de verrous sur les machines à fort nombre de cœurs.
Comprendre cette rustine demande de distinguer deux chemins d’allocation. Le chemin générique échange une sheaf vide contre une sheaf pleine tirée de la « grange » — le réservoir central des sheaves remplies. Le chemin de préremplissage, lui, recharge une sheaf qui n’est pas nécessairement vide. C’est là que se nichait le problème.
Cette architecture n’est pas née d’hier. L’allocateur SLUB, en place par défaut depuis 2007, a longtemps reposé sur des caches par CPU simples ; l’ajout des sheaves l’an dernier — un travail mené notamment par Vlastimil Babka — a introduit une couche de réutilisation à grain fin pensée pour mieux exploiter la localité de cache. C’est précisément cette couche que la rustine de Hao Li affine : elle ne change aucune interface, mais corrige un chemin de remplissage devenu sous-optimal à mesure que les sheaves se généralisaient.
Le problème : une grange qui se sature et ne se vide plus
Jusqu’à présent, quand l’API de préremplissage rechargeait une sheaf non pleine, elle ne prenait ses objets que dans les slabs partiels, jamais dans les sheaves pleines de la grange. Conséquence mécanique : une fois la liste « pleine » de la grange saturée, elle restait saturée. Aucun mécanisme ne la vidait.
Pour les objets libérés via kfree_rcu(), l’effet est direct et coûteux. Chaque sheaf RCU devait alors être vidée vers les slabs, faute de place dans la liste pleine de la grange. kfree_rcu() est massivement utilisé — il libère de la mémoire de façon différée, une fois la période de grâce RCU écoulée, dans des chemins aussi chauds que la suppression d’inodes ou la fin de connexions réseau. Forcer une vidange vers les slabs à chaque fois, c’est retomber dans le coût que les sheaves étaient censées éliminer.
La rustine : recharger depuis la grange, avec une sheaf partielle
Le changement de Hao Li inverse la priorité. Le préremplissage commence par recharger depuis la grange, puis introduit une sheaf partielle dans la grange pour y conserver les objets restants. Concrètement : la sheaf est rechargée en copiant les objets depuis la sheaf partielle, puis, si elle n’est toujours pas pleine, depuis une sheaf pleine tirée de la grange. Une sheaf dont on a copié les objets reste dans la grange comme sheaf partielle si elle contient encore des objets, ou rejoint la liste vide si elle est vide. La sheaf en cours de rechargement n’est jamais remplacée.
Le gain vient de deux côtés. D’abord, chaque sheaf pleine retirée libère de la place dans la liste pleine de la grange pour une future sheaf RCU — la saturation se résorbe au lieu de se figer. Ensuite, recharger depuis la grange coûte moins cher que recharger depuis des slabs partiels sous le verrou list_lock. Hao Li résume : « le gain vient de deux côtés : chaque sheaf pleine retirée fait de la place sur la liste pleine de la grange pour une future sheaf RCU, et recharger depuis la grange est moins coûteux que depuis des slabs partiels sous list_lock. »
Les chiffres : 24 % sur un benchmark réaliste
Les mesures publiées avec la rustine montrent une amélioration globale de 24 % sur le test will-it-scale mmap1 — un benchmark qui sollicite l’allocation mémoire de façon réaliste, et non un microbenchmark synthétique. Sur les microbenchmarks ciblés de la grange, les gains sont spectaculaires : barn_get et barn_put affichent des pourcentages à huit chiffres, dont la lecture brute est trompeuse car la ligne de base est proche de zéro, et les autres métriques progressent de plusieurs dizaines de pour cent.
Le contraste entre le +24 % réaliste et les pourcentages vertigineux des microbenchmarks est en soi une leçon : c’est le chiffre will-it-scale qui compte pour une charge de production, les autres servent à valider que le chemin critique est bien celui qu’on croit. Et tout cela pour 81 lignes de code nouveau — une densité de gain rare, même dans le sous-système mémoire.
Pourquoi cela compte en production
La gestion mémoire du noyau est un coût fixe de presque toutes les charges datacenter. Les améliorations de l’allocateur ne se voient pas dans un benchmark applicatif unique ; elles se cumulent sur chaque kmalloc, chaque kfree_rcu, chaque ouverture de fichier. Une réduction de la contention sur list_lock profite d’abord aux machines à fort nombre de cœurs, précisément celles où la saturation de la grange se manifeste le plus tôt.
Le correctif est destiné à Linux 7.4, dont la fenêtre de fusion ouvre fin octobre. Il ne changera donc rien aux distributions actuelles — Ubuntu 26.10 embarque Linux 7.3 — mais il dessine la trajectoire : l’allocateur continue de se spécialiser autour des sheaves, et les optimisations de cette couche deviennent un levier de performance à part entière, au même titre que l’ordonnanceur ou le réseau. Pour vérifier l’allocateur actif et observer l’état des slabs sur une machine, deux commandes suffisent :
grep CONFIG_SLAB /boot/config-$(uname -r)
cat /proc/slabinfo | head Verdict
Si vous suivez les versions de développement du noyau ou préparez une migration vers Linux 7.4, retenez cette rustine comme un gain mémoire à coût quasi nul — 81 lignes, aucune modification d’interface, un bénéfice mesuré sur un chemin réaliste. Si vous exploitez des charges fortement multithread et sensibles à la latence d’allocation, surveillez l’arrivée de ce changement dans les noyaux stables et testez-le sur vos microbenchmarks internes : c’est le type d’optimisation qui se traduit en économies réelles de CPU sans changer une ligne d’application. Si vous n’êtes pas concerné par le développement noyau, l’enseignement est ailleurs : la performance mémoire se joue désormais dans la couche des sheaves, et les gains « gratuits » de l’allocateur sont loin d’être épuisés. Une saturation qui se figeait silencieusement vient de trouver sa soupape — c’est le genre de correctif qui ne fait pas la une, mais qui rend tout le reste plus rapide.