9 points par GN⁺ 2025-06-14 | 2 commentaires | Partager sur WhatsApp
  • Mémoire rétrospective écrite par Jason Evans, principal artisan du développement de jemalloc, qui revient sur près de 20 ans d’évolution de jemalloc en cinq phases, en relatant avec franchise les réussites, les échecs, les limites d’un projet open source et son déclin lié aux changements internes en entreprise
  • L’allocateur mémoire jemalloc a été lancé en 2004 et a été largement utilisé pendant près de 20 ans, mais son développement officiel s’est récemment arrêté en raison de changements internes chez Meta
  • Son intégration initiale à FreeBSD, la résolution de problèmes de performance dans Firefox et son adoption à grande échelle chez Facebook ont permis d’accumuler une solide expérience en optimisation des performances et en portage
  • En traversant divers défis, notamment les problèmes de fragmentation mémoire et de montée en charge, jemalloc s’est enrichi de nombreuses fonctionnalités comme la collecte de statistiques et une infrastructure de test
  • Avec l’évolution de la culture d’entreprise chez Meta, la baisse des investissements techniques et l’absence de mainteneurs clés ont conduit à l’arrêt du développement à long terme
  • La suppression de Valgrind ou encore l’absence de retours d’utilisateurs externes ont également révélé des limites structurelles dans l’écosystème open source
  • De nouvelles évolutions restent possibles via des forks, mais le développement principal officiel ne devrait plus reprendre

Aperçu de jemalloc

  • jemalloc est un allocateur mémoire open source conçu pour la première fois en 2004, développé pour améliorer les performances et la scalabilité, et largement utilisé pendant 20 ans dans de grands projets open source ainsi que dans les infrastructures des grandes entreprises tech
  • Son développement principal ayant récemment cessé, la suite devrait passer par des forks ou une maintenance assurée par des organisations individuelles

Phase 0: Lyken

  • En 2004, son développement a commencé par un allocateur mémoire manuel dans le cadre de Lyken, un langage de programmation destiné au calcul scientifique
  • L’allocateur interne de Lyken a atteint une forme fonctionnellement complète en mai 2005
  • L’allocateur a ensuite été intégré à FreeBSD, puis retiré de Lyken, où il n’est resté qu’un mince wrapper autour de l’allocateur système
  • La raison de ce retrait est qu’après l’intégration dans FreeBSD, seules les limites de l’allocateur système sont devenues clairement visibles, notamment l’absence de suivi des allocations par thread
  • Fait amusant, la collecte de statistiques dont Lyken avait besoin à l’époque a été ajoutée plus tard à jemalloc

Phase 1: FreeBSD

  • En 2005, alors que les ordinateurs multiprocesseurs se généralisaient, FreeBSD utilisait phkmalloc, mais celui-ci n’était pas adapté aux environnements à threads parallèles
  • L’allocateur de Lyken ayant montré des avantages clairs en matière de scalabilité, il a été intégré à FreeBSD et a rapidement reçu le nom de jemalloc
  • Cependant, de graves problèmes de fragmentation sont apparus dans des applications comme KDE, au point de faire douter de sa viabilité
  • La fragmentation provenait d’un mode d’allocation dans un espace unifié sans distinction de taille ; après étude, l’architecture a été profondément revue avec un algorithme séparant les zones par taille
  • Le contenu de ce travail a été documenté dans un article présenté à BSDCan en 2006

Phase 1.5: Firefox

  • En 2007, à l’approche de la sortie de Mozilla Firefox 3, la fragmentation mémoire était un problème majeur sous Windows
  • Le portage de jemalloc vers Linux a été simple, mais Windows s’est révélé bien plus complexe
  • Mozilla a forké jemalloc depuis la version stockée dans la libc de FreeBSD afin d’améliorer sa portabilité et sa compatibilité
  • Avec le temps, Mozilla a contribué en amont de nombreuses améliorations à jemalloc, mais la version forkée est toujours restée plus performante
  • Il n’est pas clair si cela venait de régressions de performance ou d’optimisations spécifiques à certains environnements

Phase 2: Facebook

  • Lors de son arrivée chez Facebook en 2009, le principal frein de jemalloc était le manque d’outils, notamment pour le profiling et la détection de fuites mémoire
  • Pour y remédier, une fonctionnalité de profilage du tas compatible pprof a été introduite dans jemalloc 1.0.0
  • Après la migration du développement vers GitHub, de nombreuses améliorations ont été réalisées avec les utilisateurs et contributeurs externes, notamment une infrastructure de test, le support de Valgrind, des statistiques JSON et une nouvelle gestion des pages
  • En interne, l’énorme système de télémétrie de Facebook a grandement aidé à l’optimisation des performances et à la prévention des régressions
  • 3.x : introduction de l’infrastructure de test et du support de Valgrind
  • 4.x : ajout du purging basé sur le decay et des statistiques JSON
  • 5.x : passage d’une conception basée sur les chunks à une architecture fondée sur les extents, préparant l’exploitation des huge pages de 2MiB
  • L’analyse des performances basée sur la télémétrie interne de Facebook a joué un rôle décisif dans les optimisations
  • Dans la version 5.0.0, la suppression du support de Valgrind a été décidée car il n’était pas utilisé en interne, mais elle a suscité une forte opposition externe, notamment de la part de développeurs Rust
  • Par la suite, les changements organisationnels chez Facebook/Meta ont réduit l’équipe jemalloc, et la stratégie de l’entreprise a basculé d’un investissement dans les technologies clés vers une logique davantage centrée sur l’efficacité
  • En conséquence, le développement de grandes fonctionnalités comme Huge Page Allocation a stagné, et certains travaux n’ont jamais été finalisés
  • Après le départ d’Evans en 2017, Qi Wang a maintenu le développement pendant plusieurs années
  • Même après la transmission du leadership, plusieurs contributeurs ont continué à faire vivre le projet, mais sans responsable porteur d’une vision de long terme

Phase 4: Stase

  • Aujourd’hui, le développement upstream de jemalloc est terminé, et Meta suit désormais une orientation distincte selon ses besoins internes
  • La dette technique de la base de code existante est importante, et un vaste refactoring serait nécessaire au préalable
  • Les besoins de Facebook/Meta et ceux des utilisateurs externes ne coïncident plus
  • Si le développement devait reprendre, il faudrait d’abord consacrer des centaines d’heures au nettoyage de la dette technique, ce que l’auteur n’a plus la motivation de faire
  • À partir de la branche dev ou de la base 5.3.0, des forks externes restent possibles, et un nouveau projet fondé sur un fork pourrait donc émerger à tout moment

Retour d’expérience et enseignements

  • Le conflit provoqué par la suppression du support de Valgrind venait d’une mauvaise connaissance des usages externes
  • L’auteur n’a appris que deux ans plus tard que jemalloc était utilisé sur Android
  • Le projet était entièrement ouvert sur GitHub, mais les contributeurs clés issus d’organisations externes n’ont pas réussi à s’inscrire dans la durée
    • Que ce soit Mike Hommey pour Firefox ou la tentative de migration vers CMake, tout est resté inachevé
  • D’après cette expérience, le simple fait d’ouvrir un projet ne suffit pas à en faire un projet indépendant durable
  • L’auteur souligne que l’open source ne se maintient pas par la seule publication du code : il faut former des contributeurs centraux et assurer une vraie gouvernance

Mot de conclusion

  • jemalloc a été une expérience particulière même pour son auteur, partisan du garbage collection depuis plus de 25 ans
  • Il se reconcentre désormais sur le développement de systèmes de garbage collection, tout en adressant de profonds remerciements à toutes celles et ceux qui ont contribué à jemalloc

2 commentaires

 
ganadist 2025-06-14

Il existe aussi une traduction de l’article complet.
https://rosettalens.com/s/ko/jemalloc-postmortem

 
GN⁺ 2025-06-14
Commentaires sur Hacker News
  • Je comprends la décision d’archiver le dépôt upstream. Avant de quitter Meta, notre équipe jemalloc n’avait tout simplement pas la capacité de répondre à tous les problèmes remontés sur GitHub — par exemple, quelqu’un avait ouvert un ticket parce que la suite de tests ne passait pas sur Itanium, ce qui m’avait un peu fait rire. Malgré tout, c’est triste de voir la situation actuelle. Je continue à penser que jemalloc est l’implémentation générique de malloc la plus performante et la plus simple à utiliser. TCMalloc est excellent aussi, mais si on n’utilise pas bazel, c’est vraiment pénible à adopter à mon avis (de nos jours, bazel 7.4.0 a ajouté cc_static_library, ce qui facilite un peu l’export en bibliothèque statique, mais cela reste un point important). J’ai l’intention de demander à Qi s’il serait possible de faire une dernière release 6.0 avant de réarchiver le dépôt. Pour une dernière release, ce serait bien de moderniser un peu les paramètres par défaut. Par exemple, ce serait une grosse amélioration de désactiver par défaut l’option « cache oblivious », dont le nom prête à confusion, afin d’éviter que la classe de taille 16 KiB gonfle inutilement à 20 KiB. Ce n’est pas pour critiquer le choix initial de Jason : quand j’en avais discuté avec Qi et David à l’époque, le fait que l’associativité du TLB était globalement bien plus faible qu’aujourd’hui constituait une justification raisonnable. Dans le même esprit, augmenter la page size par défaut de 4 KiB à une valeur plus grande, disons 16 KiB, serait aussi un bon changement. Cela ferait passer de 16 KiB à 64 KiB le seuil à partir duquel les grandes classes de taille cessent d’être allouées dans des slabs communs pour être placées dans des plages dédiées, avec un effet significatif. Avant de quitter Meta, j’avais examiné l’idée d’appliquer ce changement à un service interne important : c’était une optimisation qui réduisait l’usage CPU de plusieurs pourcents, au prix d’une légère hausse mémoire due à la fragmentation RAM. Il y a aussi d’autres choses que j’aimerais changer (par exemple, faire passer metadata_thp de disabled à auto par défaut, ou modifier le dimensionnement des extents de slabs pour réduire la fragmentation en acceptant environ 1 % de gaspillage au lieu d’exiger des multiples exacts de la taille de page). Mais les paramètres mentionnés plus haut seraient les changements les plus importants

    • C’est moi qui ai ouvert le ticket sur l’échec de la suite de tests sur Itanium

    • C’est exactement pour ce genre de témoignages concrets et d’insights d’initiés que je continue à venir sur Hacker News. Je suis curieux de savoir pourquoi TCMalloc est difficile à utiliser sans bazel (je pose vraiment la question par curiosité)

    • J’aimerais vraiment que ce type de connaissance interne importante soit publié sous une forme plus développée, par exemple dans de la documentation officielle ou des billets de blog. À l’heure actuelle, la documentation officielle me paraît beaucoup trop maigre. Ce serait dommage que tout le savoir-faire accumulé au fil de tant de travaux chez Meta se perde avec le temps

    • C’est un peu dommage qu’un si bon logiciel soit sous-utilisé simplement parce que le processus de build et d’intégration est complexe

    • Vous avez mentionné que « l’équipe jemalloc n’avait pas assez de marge pour gérer les tickets GitHub aléatoires », et je me pose une question. J’aimerais comprendre pourquoi la gestion des issues ne fonctionnait pas bien alors qu’il semblait y avoir suffisamment de personnes chez Meta pour s’occuper du projet. Corrigez-moi si je me trompe

  • Je veux dire à quel point le travail de Jason a eu un impact énorme sur notre activité. Notre entreprise est assez importante et traite chaque jour des centaines de millions d’images et de vidéos. Pendant les premières années, nous avons énormément souffert de problèmes de fragmentation mémoire. Puis un jour nous avons adopté jemalloc, et le fait de changer seulement deux lignes dans le Dockerfile a résolu tous nos problèmes. Aujourd’hui, c’est une entreprise qui réalise des dizaines de milliards de chiffre d’affaires, et nous utilisons jemalloc dans tous nos services et tous nos Dockerfiles. Un immense merci, sincèrement

    • En pratique, beaucoup de services de traitement d’images basés sur golang recommandent ou utilisent jemalloc. Parmi les trois premiers projets du thème resize-images au 2025-06-13, imaginary l’utilise ainsi dans son Dockerfile, imgproxy est aussi évoqué à ce sujet dans le dépôt d’imaginary avec référence à une documentation archivée, et imagor utilise également jemalloc ici

    • Je pose vraiment la question par curiosité, sans aucune ironie : avez-vous fait un don ? Exprimer sa gratitude en argent, n’est-ce pas la meilleure façon de remercier ?

  • À propos de l’idée selon laquelle « jemalloc a été retiré des binaires Rust plus vite que prévu », je voudrais préciser que ce problème n’était qu’un facteur parmi plusieurs. On peut voir le contexte dans ce commentaire. Et jemalloc n’a été complètement supprimé que deux ans après que la question a été soulevée pour la première fois (voir : cette PR)

    • Parmi les différents problèmes mentionnés là-bas, je trouve intéressant que la question de la taille de page codée en dur sur arm64 ne soit toujours pas résolue upstream. À cause de cela, les développeurs d’applications doivent soit fournir plusieurs binaires Linux arm64 distincts, soit abandonner le support de certaines plateformes. Je me demande si l’introduction d’une taille de page dynamique — avec application dynamique de correctifs binaires à la manière de ftrace pour des raisons de performance — aurait vraiment rendu les choses beaucoup plus lentes
  • J’ai pris l’habitude d’utiliser systématiquement jemalloc dans tous les moteurs de jeu que j’ai développés au fil des années. Sous win32, c’est bien plus rapide que l’allocateur par défaut, et c’est aussi un avantage de pouvoir utiliser le même allocateur sur toutes les plateformes. Je l’ai découvert quand jemalloc a été intégré à FreeBSD, et depuis je n’utilise plus que lui. Je suis fier de penser que beaucoup de joueurs ont pu profiter de mes jeux grâce à jemalloc

    • L’allocateur par défaut de window est vraiment mauvais. Jemalloc, c’est le meilleur
  • Très bon article — je me demande si Facebook (désormais Meta) n’utilise plus jemalloc lui-même, ou s’il est simplement en phase de maintenance. Je me demande aussi s’il y a eu un passage à tcmalloc ou à un autre allocateur. Il y avait cette phrase : « l’ingénierie infrastructure chez Facebook a déplacé son centre de gravité, des investissements dans les technologies cœur vers une logique plus centrée sur le ROI »

    • J’ai quitté Meta il y a presque deux ans (et je ne pense pas que cela ait beaucoup changé depuis), mais jemalloc est toujours utilisé, en étant lié statiquement, dans tous les binaires chez Meta. Quant à savoir s’il serait facile de passer à tcmalloc ou à un autre allocateur, la réponse est non : jemalloc est imbriqué très profondément dans l’écosystème interne de l’entreprise. Cela touche aussi bien le plumbing de télémétrie Strobelight que de très nombreuses extensions construites autour de jemalloc (par exemple des arenas manuelles utilisant directement des hooks d’extent personnalisés), sans parler de l’évolution réelle de la plupart des applications, conçues pour tirer au maximum parti des propriétés de jemalloc

    • Le grand changement récent, c’est que tous les mainteneurs de longue date de jemalloc sont partis. Mais paradoxalement, Facebook semble aujourd’hui accorder plus d’attention au projet qu’avant, et après quelques controverses récentes, j’espère qu’on pourra aller vers une direction qui prenne en compte à la fois Qi, Jason et les utilisateurs externes

    • Meta continue de développer activement son propre fork ici

  • Ce fut un honneur de pouvoir participer à ce travail qui a eu de l’influence pendant si longtemps, de Firefox à Facebook

    • Moi aussi, j’avais envie de laisser un mot de remerciement ici au bon moment. Je ne m’attendais pas à ce que ce billet paraisse aujourd’hui, mais ce fut un honneur d’avoir pu faire partie, même modestement, de cette grande aventure. Merci aussi à @je, qi, david, ainsi qu’aux contributeurs de l’époque et à ceux qui ont suivi

    • Je pense que votre leadership pour permettre à Facebook de continuer à investir dans les technologies cœur a porté ses fruits au maximum. C’est aussi grâce à cela que des innovations comme GraphQL, PyTorch ou React ont pu voir le jour

  • La citation de la FTA — « face à des choix impossibles, les gens 1) prennent de mauvaises décisions sous une pression extrême, 2) se conforment sous une pression extrême, ou 3) contournent le problème » — donne une image difficile à imaginer comme ambiance de travail

    • Mon expérience amère, c’est que presque tous les emplois que j’ai eus depuis 2008 ressemblaient à cela
  • Je pense que jemalloc est le seul allocateur capable, sur macOS, de remplacer malloc/free aussi proprement qu’avec LD_PRELOAD (du moins vers 2020). Il peut s’intercaler facilement comme allocateur par défaut via le mécanisme des zones, et il répond bien aux exigences particulières d’Apple pour les allocateurs. D’autres allocateurs third-party ont souvent échoué à cause de ces contraintes

    • Cela dit, cette approche ne peut fonctionner qu’en supposant que l’allocateur système de macOS ne change pas ses structures internes, et si je me souviens bien, Apple a effectivement cassé cela à deux reprises en les modifiant

    • Il me semble que mimalloc peut fonctionner de la même manière, mais je n’en suis pas certain

  • J’ai toujours eu l’impression que jemalloc améliorait glibc malloc sur tous les plans, et les benchmarks montrent apparemment toujours de meilleures performances. Du coup, en tant qu’observateur extérieur, je me suis demandé pourquoi ce n’était pas l’allocateur par défaut

    • Sur FreeBSD, c’est déjà le cas. Si vous voulez remplacer malloc, il est plus simple de remplacer aussi libc par celle de FreeBSD, et dans ce cas autant passer aussi au noyau FreeBSD. Au moment de la fusion avec Facebook, j’étais enthousiaste à l’idée de présenter jemalloc à des collègues, mais comme nous étions déjà sur FreeBSD, son usage paraissait tellement naturel que cela allait de soi

    • Je ne suis pas ingénieur spécialisé en allocateurs, donc ce n’est pas un avis d’expert, mais j’ai déjà parlé autrefois avec un ingénieur qui maintenait l’allocateur de l’OS. Selon lui, les allocateurs personnalisés ont tendance à avantager massivement un processus donné sur le plan de l’allocation mémoire, au détriment de l’équité globale du système. Du point de vue d’un allocateur système, quand chaque processus adopte un comportement différent, l’optimisation d’ensemble devient difficile. C’est pourquoi, dans la plupart des environnements de service, jemalloc est souvent recommandé dès lors qu’on part du principe que « seul mon processus compte ici »

    • Je ne pense pas qu’il y ait de raison technique empêchant jemalloc de devenir l’allocateur par défaut. En pratique, c’est déjà le défaut sur FreeBSD (comme l’article le mentionne). À mon sens, la question est plus politique que technique

    • jemalloc a été validé en production à très grande échelle, sa licence est très permissive et ses performances sont démontrées. Alors pourquoi continuer à s’accrocher au malloc de glibc ? Honnêtement, en dehors d’une forme de « pureté idéologique » et de l’inertie héritée du passé, je me demande qui a intérêt à maintenir cet état de fait. Pourquoi continuer à se réfugier derrière l’excuse de la « compatibilité » ?

    • Autrefois, un gros problème des allocateurs alternatifs était qu’ils ne rendaient jamais vraiment la mémoire libérée à l’OS et la conservaient sous forme de pages sales (cela a fini par être amélioré, mais c’est un bon exemple des différences de priorités entre allocateurs). Et dans la réalité, la plupart des processus n’exécutent qu’un seul thread principal ou n’ont que des threads presque inactifs. Les allocateurs optimisés pour le multithreading peuvent donc être excessifs et coûteux dans ce type d’environnement. J’aimerais aussi rappeler que la plupart des gens ne réalisent pas qu’en pratique, il y a peu de différence entre le coût, pour le noyau, de remettre une page à zéro, et le coût, pour un processus utilisateur, de la remettre à zéro pour la réutiliser en interne

  • J’aimerais plutôt qu’on ajoute le lien vers le billet de blog dans le dépôt GitHub. Je pense qu’il est important que les personnes qui visiteront le repo plus tard connaissent ce contexte et puissent s’y référer