Conception d'un Allocateur Mémoire Haute Concurrence en C++

Présentation du système d'allocation mémoire

Ce projet implémente un allocateur mémoire optimisé pour environnements multithreads, inspiré de tc-mimic (Thread-Caching Malloc). Il remplace les fonctions standard malloc et free pour améliorer l'efficacité et réduire la fragmentation.

Prérequis techniques

  • Programmation C/C++ avancée
  • Structures de données (listes chaînées, tables de hachage)
  • Gestion mémoire des systèmes d'exploitation
  • Modèle singleton et synchronisation multithread

Fonctionnement des pools mémoire

Un pool mémoire préalloue un bloc conséquent lors de l'initialisation. Les allocations ultérieures utilisent ce bloc plutôt que les appels système directs. Cette approche résout deux problèmes fondamentaux :

Optimisation des performances

Les appels système malloc impliquent des changements de contexte coûteux. Le pool effectue des opérations en espace utilisateur via des manipulations de pointeurs.

Contrôle de la fragmentation

Deux types de fragmentation sont adressés :

  • Externe : Réduite par la gestion de blocs contigus
  • Interne : Minimisée via des tailles d'allocation alignées

Pool à taille fixe

Spécialisé dans l'allocation d'objets de dimension constante, ce pool offre :

  • Allocations en temps constant (O(1))
  • Minimisation des conflits de verrous
  • Élimination de la fragmentation externe

Implémnetation du pool fixe

template<typename Objet>
class PoolFixe {
  std::vector<char*> blocs_memoire;
  char* bloc_actuel = nullptr;
  size_t octets_restants = 0;
  void* liste_libre = nullptr;

public:
  Objet* Allouer() {
    if(liste_libre) {
      void* suivant = *static_cast<void**>(liste_libre);
      Objet* objet = static_cast<Objet*>(liste_libre);
      liste_libre = suivant;
      return objet;
    }
    // Logique d'allocation de nouveau bloc
  }
  
  void Liberer(Objet* obj) {
    obj->~Objet();
    *static_cast<void**>(obj) = liste_libre;
    liste_libre = obj;
  }
};

Architecture à trois niveaux

L'allocateur utilise une hiérarchie décentralisée :

  1. Cache Thread : Sans verrou, gère les petites allocations (≤256Ko)
  2. Cache Central : Synchronisé par verrous par compartiment, équilibre la charge
  3. Cache Page : Gère les blocs mémoire au niveau système et la fusion des pages

Cache Thread

class CacheThread {
  ListeLibre listes[TAILLE_TABLE];
  
public:
  void* Allouer(size_t taille) {
    size_t index = CalculateurIndex(taille);
    if(!listes[index].Vide()) 
      return listes[index].Extraire();
    return AcquérirDuCentral(index, taille);
  }
};

Cache Central

class CacheCentral {
  ListeEtendue listes_etendues[TAILLE_TABLE];
  std::mutex verrous[TAILLE_TABLE];
  
  Etendue* ObtenirEtendue(ListeEtendue& liste, size_t taille) {
    // Logique d'acquisition d'étendue mémoire
  }
};

Structure d'étendue mémoire

struct Etendue {
  ID_PAGE id_depart;
  size_t pages_total;
  Etendue* suivant;
  Etendue* precedent;
  size_t compteur_utilisation;
  void* liste_libre;
};

Cache Page

class CachePage {
  ListeEtendue listes[PAGES_MAX + 1];
  std::mutex verrou_global;
  
public:
  Etendue* NouvelleEtendue(size_t pages) {
    if(!listes[pages].Vide())
      return listes[pages].ExtraireFront();
    // Logique de fusion et d'allocation système
  }
};

Mécanisme de fusion des pages

Le cache page combine les étendues adjacentes lors de la libération pour former des blocs plus grands, réduisant la fragmentation externe.

Tests de performance

Comparaison des temps d'exécution (cycles CPU) :

Opération new/delete Pool fixe
100k allocations 15 800 2 300
Libérations multiples 12 500 1 900

Étiquettes: Allocateur Mémoire C++ multithreading Gestion Mémoire tc-mimic

Publié le 27 juillet à 10h11