Comment malloc travailler 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 d'une 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?

45
demandé sur Wladimir Palant 2012-05-22 20:50:08

2 réponses

glibc 2.15 gère plusieurs attribution arènes. Chaque arène a sa propre serrure. Lorsqu'un thread a besoin d'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 fondamentalement à ceci (édité pour plus 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.

36
répondu NPE 2012-05-22 17:17:15

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

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, a une stratégie plus fine, comme décrit dans la réponse d'aix , qui permet plusieurs threads pour 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, donc vous devrez vérifier le code source pour voir comment cela fonctionne.

19
répondu Adam Rosenfield 2017-05-23 12:33:42