GNU/Linux >> Tutoriales Linux >  >> Linux

¿La asignación de memoria en Linux no bloquea?

Me parece que si su aplicación de interferencia estuviera usando new/delete (malloc/free), entonces la aplicación de interferencia interferiría más con la prueba de no reciclaje. Pero no sé cómo se implementa su prueba de interferencia.

Dependiendo de cómo recicle (es decir, si usa pthread mutexes, Dios no lo quiera), su código de reciclaje podría ser lento (las operaciones atómicas de gcc serían 40 veces más rápidas al implementar el reciclaje).

Malloc, en alguna variación durante mucho tiempo en al menos algunas plataformas, ha estado al tanto de los hilos. Use los interruptores del compilador en gcc para asegurarse de obtenerlo. Los algoritmos más nuevos mantienen grupos de pequeños fragmentos de memoria para cada uno subproceso, por lo que no hay o hay poco bloqueo si su subproceso tiene el elemento pequeño disponible. He simplificado demasiado esto y depende de qué malloc esté usando su sistema. Además, si vas y asignas millones de artículos para hacer una prueba... bueno, entonces no verás ese efecto, porque los grupos de artículos pequeños tienen un tamaño limitado. O tal vez lo harás. No sé. Si liberaste el artículo justo después de asignarlo, es más probable que lo veas. Los elementos pequeños liberados vuelven a las listas de elementos pequeños en lugar del montón compartido. Aunque "lo que sucede cuando el subproceso B libera un elemento asignado por el subproceso A" es un problema que puede o no ser tratado en su versión de malloc y puede no ser tratado de una manera que no bloquee. Por supuesto, si no liberaste inmediatamente durante una prueba grande, entonces el hilo tendría que volver a llenar su lista de artículos pequeños muchas veces. Eso puede bloquear si más de un hilo lo intenta. Finalmente, en algún momento, el montón de su proceso le pedirá al sistema memoria de montón, que obviamente puede bloquearse.

Entonces, ¿estás usando pequeños elementos de memoria? Para su malloc, no sé qué pequeño sería, pero si tiene menos de 1k, seguro que es pequeño. ¿Está asignando y liberando uno tras otro, o está asignando miles de nodos y luego liberando miles de nodos? ¿Su aplicación de interferencia estaba asignando? Todas estas cosas afectarán los resultados.

Cómo reciclar con operaciones atómicas (CAS =comparar e intercambiar):

Primero agregue un pNextFreeNode a su objeto de nodo. Usé void*, puedes usar tu tipo. Este código es para punteros de 32 bits, pero también funciona para 64 bits. Luego haga una pila de reciclaje global.

void *_pRecycleHead; // global head of recycle list. 

Añadir a la pila de reciclaje:

void *Old;
while (1) { // concurrency loop
  Old = _pRecycleHead;  // copy the state of the world. We operate on the copy
  pFreedNode->pNextFreeNode = Old; // chain the new node to the current head of recycled items
  if (CAS(&_pRecycleHead, Old, pFreedNode))  // switch head of recycled items to new node
    break; // success
}

eliminar de la pila:

void *Old;
while (Old = _pRecycleHead) { // concurrency loop, only look for recycled items if the head aint null
  if (CAS(&_pRecycleHead, Old, Old->pNextFreeNode))  // switch head to head->next.
    break; // success
}
pNodeYoucanUseNow = Old;

El uso de CAS significa que la operación tendrá éxito solo si el elemento que está cambiando es el valor anterior que pasa. Si hay una carrera y otro subproceso llegó primero, entonces el valor anterior será diferente. En la vida real, esta carrera ocurre muy, muy raramente. CAS es solo un poco más lento que establecer un valor, por lo que en comparación con los mutexes... es genial.

La eliminación de la pila, arriba, tiene una condición de carrera si agrega y elimina el mismo elemento rápidamente. Resolvemos eso agregando un número de versión a los datos compatibles con CAS. Si haces la versión # al mismo tiempo que el puntero a la cabeza de la pila de reciclaje, ganas. Usa un sindicato. No cuesta nada adicional a CAS 64 bits.

union TRecycle {
  struct {
    int iVersion;
    void *pRecycleHead;
  } ;  // we can set these.  Note, i didn't name this struct.  You may have to if you want ANSI
  unsigned long long n64;  // we cas this
}

Tenga en cuenta que tendrá que ir a la estructura de 128 bits para el sistema operativo de 64 bits. por lo que la pila de reciclaje global se ve así ahora:

TRecycle _RecycleHead;

Añadir a la pila de reciclaje:

while (1) { // concurrency loop
  TRecycle New,Old;
  Old.n64 = _RecycleHead.n64;  // copy state
  New.n64 = Old.n64;  // new state starts as a copy
  pFreedNode->pNextFreeNode = Old.pRecycleHead;  // link item to be recycled into recycle pile
  New.pRecycleHead = pFreedNode;  // make the new state
  New.iVersion++;  // adding item to list increments the version.
  if (CAS(&_RecycleHead.n64, Old.n64, New.n64))  // now if version changed...we fail
    break; // success
}

eliminar de la pila:

while (1) { // concurrency loop
  TRecycle New,Old;
  Old.n64 = _RecycleHead.n64;  // copy state
  New.n64 = Old.n64;  // new state starts as a copy
  New.pRecycleHead = New.pRecycledHead.pNextFreeNode;  // new will skip over first item in recycle list so we can have that item.
  New.iVersion++;  // taking an item off the list increments the version.
  if (CAS(&_RecycleHead.n64, Old.n64, New.n64))  // we fail if version is different.
    break; // success
}
pNodeYouCanUseNow = Old.pRecycledHead;

Apuesto a que si reciclas de esta manera verás un aumento en el rendimiento.


Esta pregunta tiene una serie de buenas respuestas:en C/C++ multiproceso, malloc/new bloquea el almacenamiento dinámico al asignar memoria.

El consenso allí es que hay bloqueo. Por lo tanto, una asignación grande o una que requiera un intercambio podría bloquear una asignación más pequeña en otro subproceso, incluso si la asignación más pequeña podría terminar si no fuera por la asignación más grande en curso.

gcc's new es seguro para subprocesos, si compila con soporte para pthreads, pero eso no es realmente lo que está preguntando.

Sé que en Windows puede crear su propio montón, que podría usarse para configurar la memoria al comienzo de su programa. No tengo conocimiento de ninguna llamada de Linux/Unix para hacer cosas similares.


Esto es realmente más o menos lo mismo que esta pregunta.

Básicamente, malloc no está definido para ser seguro para subprocesos, pero los implementadores son libres de agregar una implementación para que sea seguro para subprocesos. Según su descripción, parece que su versión particular es.

Sin duda, en palabras de Obi-Wan, "Usa la fuente, Luke". El malloc la fuente estará disponible y, en general, es bastante fácil de leer.

@Mark, puede obtener la fuente libc estándar de GNU

$ git clone git://sourceware.org/git/glibc.git
$ cd glibc
$ git checkout --track -b glibc-2_11-branch origin/release/2.11/master

Ver también aquí. Recuerda que malloc está en la sección 3 del manual:es una función de biblioteca, por lo que no estará en las fuentes de su kernel. Sin embargo, es posible que deba leer hacia abajo en brk ,sbrk , getrlimit y setrlimit y similares para averiguar qué hace el kernel.

Un enlace más:el proyecto GCC.

Bueno, uno más (puedo parar en cualquier momento):aquí hay una página desde la que puedes descargar las fuentes. Descomprime el archivo y deberías encontrarlo en ./malloc/malloc.c .


En sistemas multiproceso, malloc() y free() (y new / delete ) normalmente usan primitivos de sincronización para que sean seguros para llamar desde múltiples subprocesos.

Esta sincronización también afecta el rendimiento de algunas aplicaciones, en particular las aplicaciones que realizan muchas asignaciones y desasignaciones en entornos muy paralelos. Asignadores de memoria multiproceso más eficientes son un campo activo de investigación; consulte jemalloc y tcmalloc por dos conocidos.


Linux
  1. El kernel de Linux:las 5 principales innovaciones

  2. Uso de memoria de Linux

  3. Cómo borrar la caché de memoria en Linux

  4. Asesino de falta de memoria de Linux

  5. Lanzamiento de Kali Linux 2018.1

Cómo verificar el uso de memoria en Linux

Comando libre de Linux (Comprobar el uso de la memoria)

Comando libre en Linux

Cómo borrar la memoria de intercambio en Linux

Ejemplos de comandos gratuitos en Linux

Segmentación de memoria de Linux