multithreading x86 spinlock

multithreading - ¿Cómo funciona la instrucción pausa x86 en spinlock*y*puede usarse en otros escenarios?



(4)

La instrucción PAUSE también parece usarse en procesadores de hiperprocesamiento para mitigar el impacto en el rendimiento de otros subprocesos hipertexto, presumiblemente al cederles más tiempo de CPU.

El siguiente artículo de Intel describe esto y, como es lógico, recomienda evitar los bucles de espera ocupados en dichos procesadores: https://software.intel.com/en-us/articles/long-duration-spin-wait-loops-on-hyper-threading-technology-enabled-intel-processors

la instrucción de pausa se usa comúnmente en el ciclo de pruebas de spinlock de spinlock , cuando otro hilo posee el bloqueo de espín para mitigar el lazo apretado. Se dice que es equivalente a algunas instrucciones NOP. ¿Alguien podría decirme cómo funciona exactamente para la optimización de bloqueo de giro? Me parece que incluso las instrucciones NOP son una pérdida de tiempo de CPU. ¿Disminuirán el uso de CPU?

Otra pregunta es que podría usar la instrucción de pausa para otros fines similares. Por ejemplo, tengo un hilo ocupado que sigue escaneando algunos lugares (por ejemplo, una cola) para recuperar nuevos nodos; sin embargo, a veces la cola está vacía y el hilo está acabando de perder el tiempo de la CPU. Dormir el hilo y despertarlo con otros hilos puede ser una opción, sin embargo, el hilo es crítico, por lo que no quiero que duerma. ¿Podría pausar la instrucción para mi propósito de mitigar el uso de la CPU? ¿Actualmente usa 100% de CPU de un núcleo físico?

Gracias.


Un procesador sufre una grave penalización de rendimiento al salir del bucle porque detecta una posible violación de orden de memoria. La instrucción PAUSE proporciona una pista al procesador de que la secuencia de código es un ciclo de espera de giro. El procesador usa esta sugerencia para evitar la violación de orden de memoria en la mayoría de las situaciones, lo que mejora enormemente el rendimiento del procesador. Por esta razón, se recomienda que se coloque una instrucción PAUSE en todos los bucles spin-wait. Una función adicional de la instrucción PAUSE es reducir la energía consumida por los procesadores Intel.

[fuente: manual de Intel]


PAUSE notifica a la CPU que se trata de un ciclo de espera de spinlock, por lo que se pueden optimizar los accesos a memoria y caché. Consulte también la instrucción de pausa en x86 para obtener más detalles sobre cómo evitar la mala especulación del orden de memoria al salir del spin-loop.

PAUSE en realidad puede detener la CPU por un tiempo para ahorrar energía. Las CPUs antiguas lo decodifican como REP NOP, por lo que no es necesario verificar si es compatible. Las CPU antiguas simplemente no harán nada (NOP) lo más rápido posible.

Ver también https://software.intel.com/en-us/articles/benefitting-power-and-performance-sleep-loops

Actualización: no creo que sea una buena idea usar PAUSE en la verificación de colas a menos que vaya a hacer que su cola parezca como un spinlock (y no hay una manera obvia de hacerlo).

Girar durante mucho tiempo sigue siendo muy malo, incluso con PAUSA.


Intel solo recomienda usar las instrucciones PAUSE cuando el spin-loop es muy corto.

Como entendí por sus preguntas, las esperas en su caso son muy largas. En este caso, los bucles de giro no son recomendables.

Usted escribió que tiene un "hilo que sigue escaneando algunos lugares (por ejemplo, una cola) para recuperar nuevos nodos".

En tal caso, Intel recomienda utilizar las funciones de API de sincronización de su sistema operativo. Por ejemplo, puede crear un evento cuando aparece un nuevo nodo en una cola, y simplemente espere este evento usando WaitForSingleObject(Handle, INFINITE) . La cola activará este evento siempre que aparezca un nuevo nodo.

De acuerdo con el Manual de optimización de Intel, la instrucción PAUSE se usa generalmente con subprocesos de software que se ejecutan en dos procesadores lógicos ubicados en el mismo núcleo del procesador, esperando a que se libere un bloqueo. Dichos bucles de espera cortos tienden a durar entre decenas y unos pocos cientos de ciclos (es decir, 20-500 ciclos de CPU), por lo que en cuanto a rendimiento, es más beneficioso esperar mientras ocupa la CPU que ceder al sistema operativo.

500 ciclos de CPU en un procesador Core i7 7700K a 4500 MHz es 0.0000001 segundos, es decir, 1/10000000th de segundo: la CPU puede hacer 10 millones de veces por segundo este circuito de 500 ciclos de CPU.

Como puede ver, esta instrucción PAUSE es por períodos muy cortos de tiempo.

Por otro lado, cada llamada a una función de API como Sleep () experimenta el costoso costo de un cambio de contexto, que puede ser de más de 10000 ciclos; también sufre el costo del anillo 3 para anular 0 transiciones, que pueden ser más de 1000 ciclos.

Si hay más hilos, entonces los núcleos del procesador (multiplicados por la función hyperthreading, si está presente) están disponibles, y un hilo pasará a otro en el medio de una sección crítica, esperar la sección crítica de otro hilo realmente puede tardar bastante , al menos 10000+ ciclos, por lo que la instrucción PAUSE será inútil.

Por favor, consulte estos artículos para más información:

Cuando se espera que el ciclo de espera dure miles de ciclos o más, es preferible ceder al sistema operativo llamando a una de las funciones de la API de sincronización del sistema operativo, como WaitForSingleObject en el sistema operativo Windows.

Como conclusión: en su escenario, la instrucción PAUSE no será la mejor opción, ya que su tiempo de espera es largo, mientras que PAUSE está diseñado para bucles muy cortos. PAUSE es solo 131 ciclos de procesadores SkyWell o posteriores. Por ejemplo, es justo o 31.19ns en la CPU Intel Core i7-7700K @ 4.20GHz Kaby Lake.

En procesadores anteriores, como Haswell, tengo alrededor de 9 ciclos. Es 2.81ns en Intel Core i5-4430 @ 3GHz. Por lo tanto, para los bucles largos, es mejor ceder el control a otros subprocesos utilizando las funciones de API de sincronización del SO que ocupar CPU con el bucle PAUSE .