c++ c multithreading hardware

c++ - Paranoia multiproceso



multithreading hardware (9)

Esta es una pregunta compleja, por favor considere cuidadosamente antes de responder.

Considera esta situación. Dos hilos (un lector y un escritor) acceden a una única int global. ¿Esto es seguro? Normalmente, respondería sin pensar, ¡sí!

Sin embargo, me parece que Herb Sutter no lo cree así. En sus artículos sobre concurrencia efectiva, analiza una cola defectuosa sin bloqueos y la versión corregida .

Al final del primer artículo y al comienzo del segundo, analiza un rasgo de variables que rara vez se considera, el orden de escritura. Los int son atómicos, bueno, pero los ints no están necesariamente ordenados, lo que podría destruir cualquier algoritmo sin bloqueo, incluido mi escenario anterior. Estoy completamente de acuerdo en que la única forma de garantizar el comportamiento correcto de subprocesos múltiples en todas las plataformas presentes y futuras es usar átomos (barreras de memoria AKA) o mutexes.

Mi pregunta; ¿Alguna vez se ha escrito un problema en el hardware real? ¿O la paranoia multiproceso es solo pedante?
¿Qué hay de los sistemas clásicos de uniprocesador?
¿Qué hay de los procesadores RISC más simples, como un power-pc integrado?

Aclaración : estoy más interesado en lo que dijo el Sr. Sutter sobre el hardware (procesador / caché) que reordena las escrituras variables. Puedo evitar que el optimizador rompa el código con los modificadores del compilador o la inspección manual de la compilación posterior del ensamblaje. Sin embargo, me gustaría saber si el hardware puede estropear el código en la práctica.


¿Es esto un problema en el hardware real?

Absolutamente, particularmente ahora con el cambio a múltiples núcleos para CPU actuales y futuras. Si depende de la atomicidad ordenada para implementar funciones en su aplicación y no puede garantizar este requisito a través de la plataforma elegida o el uso de primitivas de sincronización, en todas las condiciones, es decir, el cliente cambia de una CPU de un solo núcleo a una CPU de múltiples núcleos , entonces solo estás esperando que ocurra un problema.

Citando del referido artículo de Herb Sutter (segundo)

Las variables atómicas ordenadas se deletrean de diferentes maneras en plataformas y entornos populares. Por ejemplo:

  • volatile en C # /. NET, como en volatile int .
  • volatile o * Atomic * en Java, como en volatile int , AtomicInteger .
  • atomic<T> en C ++ 0x, el próximo Estándar ISO C ++, como en atomic<int> .

No he visto cómo C ++ 0x implementa la atomicidad ordenada, por lo que no puedo especificar si la próxima característica del lenguaje es una implementación de biblioteca pura o también depende de los cambios en el idioma. Podría revisar la propuesta para ver si se puede incorporar como una extensión no estándar a su cadena de herramientas actual hasta que la nueva norma esté disponible, incluso puede estar disponible para su situación.


Como dijiste, debido al reordenamiento realizado a nivel de caché o procesador, en realidad necesitas algún tipo de barrera de memoria para garantizar una sincronización adecuada, especialmente para procesadores múltiples (y especialmente en plataformas que no sean x86). (Me dieron a creer que los sistemas de un solo procesador no tienen estos problemas, pero no me citan en esto --- ciertamente estoy más inclinado a jugar seguro y hacer el acceso sincronizado de todos modos).


Es un problema en el hardware real. Un amigo mío trabaja para IBM y se gana la vida principalmente al suscribir este tipo de problemas en los códigos de los clientes.

Si desea ver qué tan malas pueden ser las cosas, busque documentos académicos sobre el Modelo de memoria Java (y también ahora el modelo de memoria C ++). Dado el reordenamiento que puede hacer el hardware real, tratar de descubrir qué es seguro en un lenguaje de alto nivel es una pesadilla.


Hemos encontrado el problema, aunque en procesadores Itanium donde el reordenamiento de la instrucción es más agresivo que x86 / x64.

La solución era usar una instrucción Interlocked ya que no había (en ese momento) forma de decirle simplemente al compilador una barrera de escritura después de la asignación.

Realmente necesitamos una extensión de lenguaje para lidiar con esto limpiamente. El uso de volátil (si es compatible con el compilador) es demasiado grueso para los casos en los que intentas exprimir al máximo el rendimiento de un fragmento de código.


Sí, utilice barreras de memoria para evitar que se reordenen las instrucciones cuando sea necesario. En algunos compiladores de C ++, la palabra clave volátil se ha expandido para insertar barreras de memoria implícitas para cada lectura y escritura, pero esta no es una solución portátil. (Del mismo modo con las API winlock * win32). Vista incluso agrega algunas API enclavadas más finas que le permiten especificar la semántica de lectura o escritura.

Desafortunadamente, C ++ tiene un modelo de memoria tan flexible que cualquier tipo de código como este va a ser no portátil hasta cierto punto y tendrás que escribir diferentes versiones para diferentes plataformas.


Su idea de inspeccionar el conjunto no es lo suficientemente buena; el reordenamiento puede ocurrir en el nivel de hardware.

Para responder a su pregunta "¿este es un problema en el hardware de lectura ?:" ¡Sí! De hecho, me he encontrado con ese problema yo mismo.

¿Está bien evitar el problema con los sistemas de uniprocesador u otras situaciones de casos especiales? Yo diría que "no" porque en cinco años a partir de ahora es posible que tenga que ejecutar en multi-core después de todo, y luego encontrar todas estas ubicaciones será complicado (¿imposible?).

Una excepción: software diseñado para aplicaciones de hardware integradas donde de hecho tiene un control total sobre el hardware. De hecho, he "engañado" así en esas situaciones, por ejemplo, en un procesador ARM.


La respuesta a la pregunta "¿es seguro?" Es intrínsecamente ambigua.

Siempre es seguro, incluso para los dobles, en el sentido de que su computadora no se incendiará. Es seguro, en el sentido de que siempre obtendrás un valor que el int mantenía en algún momento del pasado, no es seguro, en el sentido de que puedes obtener un valor que es / será actualizado por otro hilo.

"Atómico" significa que obtienes la segunda garantía. Como el doble generalmente no es atómico, podrías obtener 32 bits viejos y 32 nuevos. Eso es claramente inseguro.


No, esto no es seguro y hay hardware real disponible que muestra este problema, por ejemplo, el modelo de memoria en el chip powerpc en xbox 360 permite reordenar las escrituras. Esto se ve agravado por la falta de barreras en los intrínsecos, consulte este artículo en msdn para obtener más detalles.


Cuando hice la pregunta, me interesó más uniprocessor powerpc. En uno de los comentarios, InSciTek Jeff mencionó las instrucciones powerpc SYNC e ISYNC. Aquellos donde la clave para una respuesta definitiva. Lo encontré aquí en el sitio de IBM.

El artículo es grande y bastante denso, pero el artículo es No, no es seguro. En powerpc anteriores, los optimizadores de memoria no eran lo suficientemente sofisticados como para causar problemas en un uniprocesador. Sin embargo, los más nuevos son mucho más agresivos y pueden incluso romper el acceso simple a un int global.