¿Cómo funciona malloc en un entorno multiproceso?

¿El típico malloc (para la plataforma x86-64 y el sistema operativo Linux) bloquea ingenuamente un mutex al principio y lo libera cuando termina, o bloquea un mutex de una manera más inteligente a un nivel más fino, de modo que se reduce la contención de bloqueo. Si efectivamente lo hace de la segunda manera, ¿cómo lo hace?

preguntado el 22 de mayo de 12 a las 17:05

¿Cuál es el contexto donde has visto eso? ¿algún código citado o referencia? -

suavemente: estoy preguntando, no diciendo. -

2 Respuestas

glibc 2.15 opera asignación múltiple arenas. Cada arena tiene su propia cerradura. Cuando un subproceso necesita asignar memoria, malloc() elige una arena, la bloquea y le asigna memoria.

El mecanismo para elegir una arena es algo elaborado y tiene como objetivo reducir la contención de bloqueo:

/* 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. */

Con esto en mente, malloc() básicamente se ve así (editado por brevedad):

  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;

Este asignador se llama ptmalloc. Está basado en trabajo anterior por Doug Lea, y es mantenido por Wolfram Gloger.

contestado el 22 de mayo de 12 a las 18:05

Doug Lea's malloc utilizó bloqueo grueso (o ningún bloqueo, dependiendo de los ajustes de configuración), donde cada llamada a malloc/realloc/free está protegido por un mutex global. Esto es seguro pero puede ser ineficaz en entornos altamente multiproceso.

ptmalloc3, que es el predeterminado malloc implementación en la biblioteca GNU C (libc) utilizada en la mayoría de los sistemas Linux en estos días, tiene una estrategia más detallada, como se describe en la respuesta de aix, que permite que varios subprocesos asignen memoria de forma simultánea y segura.

nedmalloc es otra implementación independiente que reclama un rendimiento multiproceso aún mejor que ptmalloc3 y varios otros asignadores. No sé cómo funciona, y no parece haber ninguna documentación obvia, así que tendrás que revisar el código fuente para ver cómo funciona.

contestado el 23 de mayo de 17 a las 13:05

Todavía estoy indeciso sobre si nedmalloc es una verdadera hazaña de ingeniería o spam de SEO... :-) - R .. GitHub DEJA DE AYUDAR A ICE

también tcmalloc de google que usa bloqueos en cubos del tamaño de su solicitud. Mejor rendimiento de subprocesos con menos contención, más asignación excedente. - otto malvado

@R..: Parece un poco sospechoso a primera vista, pero tiene un código fuente para que pueda compararlo usted mismo (no lo he hecho). Doug Lea también dice "Si está usando malloc en un programa concurrente, considere usar nedmalloc o ptmalloc" en los comentarios de dlmalloc.c. Así que creo que probablemente sea legítimo. - adam rosenfield

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.