web-dev-qa-db-fra.com

Comment fonctionne malloc dans un environnement multithread?

Est-ce que le malloc typique (pour la plate-forme x86-64 et le système d'exploitation Linux) verrouille naïvement un mutex au début et le libère une fois terminé, ou verrouille-t-il un mutex de manière plus intelligente à un niveau plus fin, de sorte que la contention de verrouillage est réduite? S'il le fait effectivement de la deuxième façon, comment le fait-il?

49
pythonic

glibc 2.15 Exploite plusieurs arènes d'allocation . Chaque arène a sa propre serrure. Lorsqu'un thread doit allouer de la mémoire, malloc() sélectionne une arène, la verrouille et lui alloue de la mémoire.

Le mécanisme de choix d'une arène est quelque peu élaboré et vise à réduire les conflits de verrouillage:

/* arena_get() acquires an arena and locks the corresponding mutex.
   First, try the one last locked successfully by this thread.  (This
   is the common case and handled with a macro for speed.)  Then, loop
   once over the circularly linked list of arenas.  If no arena is
   readily available, create a new one.  In this latter case, `size'
   is just a hint as to how much memory will be required immediately
   in the new arena. */

Dans cet esprit, malloc() ressemble essentiellement à ceci (édité par souci de concision):

  mstate ar_ptr;
  void *victim;

  arena_lookup(ar_ptr);
  arena_lock(ar_ptr, bytes);
  if(!ar_ptr)
    return 0;
  victim = _int_malloc(ar_ptr, bytes);
  if(!victim) {
    /* Maybe the failure is due to running out of mmapped areas. */
    if(ar_ptr != &main_arena) {
      (void)mutex_unlock(&ar_ptr->mutex);
      ar_ptr = &main_arena;
      (void)mutex_lock(&ar_ptr->mutex);
      victim = _int_malloc(ar_ptr, bytes);
      (void)mutex_unlock(&ar_ptr->mutex);
    } else {
      /* ... or sbrk() has failed and there is still a chance to mmap() */
      ar_ptr = arena_get2(ar_ptr->next ? ar_ptr : 0, bytes);
      (void)mutex_unlock(&main_arena.mutex);
      if(ar_ptr) {
        victim = _int_malloc(ar_ptr, bytes);
        (void)mutex_unlock(&ar_ptr->mutex);
      }
    }
  } else
    (void)mutex_unlock(&ar_ptr->mutex);

  return victim;

Cet allocateur est appelé ptmalloc . Il est basé sur travaux antérieurs par Doug Lea, et est maintenu par Wolfram Gloger.

38
NPE

malloc de Doug Lea utilisait un verrouillage grossier (ou pas de verrouillage, selon les paramètres de configuration), où chaque appel à malloc/realloc/free est protégé par un mutex global. Ceci est sûr mais peut être inefficace dans des environnements hautement multithreads.

ptmalloc3 , qui est l'implémentation par défaut malloc dans la bibliothèque GNU C (libc) utilisée sur la plupart des systèmes Linux de nos jours, possède un une stratégie plus fine, comme décrit dans la réponse d'Aix , qui permet à plusieurs threads d'allouer simultanément de la mémoire en toute sécurité.

nedmalloc est une autre implémentation indépendante qui revendique des performances multithread encore meilleures que ptmalloc3 et divers autres allocateurs. Je ne sais pas comment cela fonctionne, et il ne semble pas y avoir de documentation évidente, vous devrez donc vérifier le code source pour voir comment cela fonctionne.

20
Adam Rosenfield