Firefox 3 est venu avec un nouvel allocateur: jemalloc
.
J'ai entendu à plusieurs endroits que ce nouveau répartiteur est meilleur. Les meilleurs résultats de Google ne donnent cependant aucune information supplémentaire et je suis intéressé par la façon dont cela fonctionne exactement.
jemalloc
est apparu pour la première fois pour FreeBSD, l'idée originale d'un "Jason Evans", d'où le "je". Je le ridiculiserais d'être égoïste si je n'avais pas écrit une seule fois un système d'exploitation appelé paxos
:-)
Voir ce PDF pour plus de détails. Il s'agit d'un livre blanc décrivant en détail le fonctionnement des algorithmes.
Le principal avantage est l'évolutivité dans les systèmes multiprocesseurs et multi-threads obtenue, en partie, en utilisant plusieurs arènes (les morceaux de mémoire brute à partir desquels les allocations sont faites).
Dans les situations à un seul thread, il n'y a aucun avantage réel à plusieurs arènes, donc une seule arène est utilisée.
Cependant, dans des situations à plusieurs threads, de nombreuses arènes sont créées (quatre fois plus d'arènes qu'il y a de processeurs) et des threads sont attribués à ces arènes de manière circulaire.
Cela signifie que la contention des verrous peut être réduite car, alors que plusieurs threads peuvent appeler malloc
ou free
simultanément, ils ne s'affronteront que s'ils partagent la même arène. Deux threads avec des arènes différentes ne s'influenceront pas.
De plus, jemalloc
essaie d'optimiser la localisation du cache car le fait d'extraire des données de RAM est beaucoup plus lent que d'utiliser des données déjà présentes dans les caches CPU (pas de concept différent du différence entre l'extraction rapide à partir de RAM et l'extraction lente à partir du disque). À cette fin, il essaie d'abord de minimiser l'utilisation globale de la mémoire, car cela est plus susceptible de garantir que l'ensemble de travail de l'application est en cache.
Et, lorsque cela n'est pas possible, il essaie de garantir que les allocations sont contiguës, car la mémoire allouée ensemble a tendance à être utilisée ensemble.
D'après le livre blanc, ces stratégies semblent donner des performances similaires aux meilleurs algorithmes actuels pour une utilisation à un seul thread tout en offrant des améliorations pour une utilisation à plusieurs threads.
Il existe une source intéressante: la source C elle-même: https://dxr.mozilla.org/mozilla-central/source/memory/build/mozjemalloc.cpp ( old =)
Au début, un bref résumé décrit comment cela fonctionne à peu près.
// This allocator implementation is designed to provide scalable performance
// for multi-threaded programs on multi-processor systems. The following
// features are included for this purpose:
//
// + Multiple arenas are used if there are multiple CPUs, which reduces lock
// contention and cache sloshing.
//
// + Cache line sharing between arenas is avoided for internal data
// structures.
//
// + Memory is managed in chunks and runs (chunks can be split into runs),
// rather than as individual pages. This provides a constant-time
// mechanism for associating allocations with particular arenas.
//
// Allocation requests are rounded up to the nearest size class, and no record
// of the original request size is maintained. Allocations are broken into
// categories according to size class. Assuming runtime defaults, 4 kB pages
// and a 16 byte quantum on a 32-bit system, the size classes in each category
// are as follows:
//
// |=====================================|
// | Category | Subcategory | Size |
// |=====================================|
// | Small | Tiny | 4 |
// | | | 8 |
// | |----------------+---------|
// | | Quantum-spaced | 16 |
// | | | 32 |
// | | | 48 |
// | | | ... |
// | | | 480 |
// | | | 496 |
// | | | 512 |
// | |----------------+---------|
// | | Sub-page | 1 kB |
// | | | 2 kB |
// |=====================================|
// | Large | 4 kB |
// | | 8 kB |
// | | 12 kB |
// | | ... |
// | | 1012 kB |
// | | 1016 kB |
// | | 1020 kB |
// |=====================================|
// | Huge | 1 MB |
// | | 2 MB |
// | | 3 MB |
// | | ... |
// |=====================================|
//
// NOTE: Due to Mozilla bug 691003, we cannot reserve less than one Word for an
// allocation on Linux or Mac. So on 32-bit *nix, the smallest bucket size is
// 4 bytes, and on 64-bit, the smallest bucket size is 8 bytes.
//
// A different mechanism is used for each category:
//
// Small : Each size class is segregated into its own set of runs. Each run
// maintains a bitmap of which regions are free/allocated.
//
// Large : Each allocation is backed by a dedicated run. Metadata are stored
// in the associated arena chunk header maps.
//
// Huge : Each allocation is backed by a dedicated contiguous set of chunks.
// Metadata are stored in a separate red-black tree.
//
// *****************************************************************************
Cependant, une analyse d'algorithme plus approfondie manque.
Quant aux avantages que jemalloc a apportés à mozilla, par http://blog.pavlov.net/2008/03/11/firefox-3-memory-usage/ (également le premier résultat Google pour mozilla + jemalloc ):
[...] a conclu que jemalloc nous avait donné la plus petite quantité de fragmentation après avoir fonctionné pendant une longue période. [...] Nos tests automatisés sur Windows Vista ont montré une baisse de 22% de l'utilisation de la mémoire lorsque nous avons activé jemalloc.
Aerospike a implémenté jemalloc dans une branche privée en 2013. En 2014, il a été intégré à Aerospike 3.3. Psi Mankoski vient d'écrire sur l'implémentation d'Aerospike, plus quand et comment utiliser efficacement jemalloc, pour High Scalability .
jemalloc a vraiment aidé Aerospike à tirer parti des architectures informatiques modernes multithread, multi-CPU et multi-core. Il existe également des capacités de débogage très importantes intégrées à jemalloc pour gérer les arènes. Le débogage a permis à Psi de pouvoir dire, par exemple, ce qui était une véritable fuite de mémoire, par rapport à ce qui était le résultat de la fragmentation de la mémoire. Psi explique également comment le cache de threads et l'allocation par thread ont fourni une amélioration globale des performances (vitesse).