me parece si su aplicación de interferencia utilizaba 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 reciclas (es decir, si usas pthread mutexes, dios no lo quiera) tu código de reciclaje podría ser lento (gcc atomic ops sería 40 veces más rápido implementando reciclaje).
Malloc, en alguna variación desde hace mucho tiempo en al menos algunas plataformas, ha tenido conocimiento de los hilos. Use el compilador enciende gcc para asegurarse de obtenerlo. Los algoritmos más recientes mantienen agrupaciones de fragmentos de memoria pequeños para cada uno de los hilos, por lo que no hay ningún bloqueo o poco si su subproceso tiene el pequeño elemento disponible. Lo simplifiqué en exceso y depende de qué malloc esté usando su sistema. Además, si va y asigna millones de elementos para hacer una prueba ... bueno, entonces no verá ese efecto, porque los grupos de elementos pequeños tienen un tamaño limitado. O tal vez lo harás. No lo sé. Si liberaste el artículo justo después de la asignación, es más probable que lo veas. Los artículos pequeños liberados vuelven a las listas de artículos pequeños en lugar del montón compartido. Aunque "lo que sucede cuando el hilo B libera un elemento asignado por el hilo A" es un problema que puede o no tratarse en su versión de malloc y no se puede tratar de una manera no bloqueante. Por supuesto, si no lo liberó inmediatamente durante una prueba grande, entonces el hilo tendría que volver a llenar su lista de elementos pequeños muchas veces. Eso puede bloquear si lo intenta más de un hilo.Finalmente, en algún punto, el montón de su proceso solicitará al sistema la memoria dinámica, que obviamente puede bloquear.
¿Está utilizando elementos de memoria pequeña? Para su Malloc, no sé lo pequeño que sería, pero si usted es < 1k, eso es ciertamente pequeño. ¿Estás asignando y liberando uno después del otro, o asignando miles de nodos y luego liberando miles de nodos? ¿Estaba asignando su aplicación de interferencia? Todas estas cosas afectarán los resultados.
cómo reciclar con operaciones atómicas (CAS = comparar y swap):
primero Añadir un pNextFreeNode a su objeto de nodo. Utilicé void *, puedes usar tu tipo. Este código es para punteros de 32 bits, pero también funciona para 64 bits. Luego haz una pila de reciclaje global.
void *_pRecycleHead; // global head of recycle list.
Agregar a reciclan la pila:
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
}
quitar 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;
Usando CAS significa que la operación tendrá éxito solamente si el artículo que está cambiando es el valor antiguo que pase in. Si hay una carrera y otro subía 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 el hecho de establecer un valor en comparación con los mutexes ... se mece.
La eliminación de la pila, arriba, tiene una condición de carrera si agrega y quita el mismo elemento rápidamente. Lo resolvemos agregando una versión # a los datos de CAS'able. Si haces la versión # al mismo tiempo que el puntero a la cabeza de la pila de reciclaje, ganas. Usa una unión. No cuesta nada extra para 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
}
Nota, Tendrás que ir a la estructura de 128 bit para 64 bit OS. por lo que la pila reciclaje mundial se parece a esto ahora:
TRecycle _RecycleHead;
Agregar a reciclan 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
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 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 usted recicla esta manera verá un aumento de perf.
Interesante pregunta. Sin embargo, "no bloqueo" no es la palabra correcta, creo. El hilo que pide memoria está, por supuesto, bloqueado hasta que recibe la memoria. Lo que está preguntando es si otros hilos también serían bloqueados en sus asignaciones de memoria (mi suposición es sí, ya que la memoria del montón es un recurso compartido). No tiene un buen término para eso, tal vez "simultaneidad de asignación de memoria". – Thilo
"sin bloqueo" es la terminología correcta. Los algoritmos simultáneos se incluyen en las clases de bloqueo, bloqueo, no bloqueo o espera libre. Los algoritmos de bloqueo son obvios; sin embargo, hay sutiles distinciones entre las últimas tres clases. – Mark
Todo depende. Algunos sistemas tienen diferentes versiones de la biblioteca estándar que están vinculadas con el ejecutable si el enhebrado está habilitado. –