stringtranslate.com

Barrera de la memoria

En informática , una barrera de memoria , también conocida como membar , barrera de memoria o instrucción de barrera , es un tipo de instrucción de barrera que hace que una unidad central de procesamiento (CPU) o un compilador imponga una restricción de orden en las operaciones de memoria emitidas antes y después de la barrera. instrucción. Por lo general, esto significa que se garantiza que las operaciones emitidas antes de la barrera se realizarán antes que las operaciones emitidas después de la barrera.

Las barreras de memoria son necesarias porque la mayoría de las CPU modernas emplean optimizaciones de rendimiento que pueden provocar una ejecución desordenada . Esta reordenación de las operaciones de memoria (cargas y almacenes) normalmente pasa desapercibida dentro de un único hilo de ejecución , pero puede provocar un comportamiento impredecible en programas y controladores de dispositivos concurrentes a menos que se controle cuidadosamente. La naturaleza exacta de una restricción de orden depende del hardware y está definida por el modelo de ordenación de la memoria de la arquitectura . Algunas arquitecturas proporcionan múltiples barreras para imponer diferentes restricciones de orden.

Las barreras de memoria se utilizan normalmente cuando se implementa código de máquina de bajo nivel que opera en la memoria compartida por varios dispositivos. Dicho código incluye primitivas de sincronización y estructuras de datos sin bloqueo en sistemas multiprocesador y controladores de dispositivos que se comunican con el hardware de la computadora .

Ejemplo

Cuando un programa se ejecuta en una máquina con una sola CPU, el hardware realiza la contabilidad necesaria para garantizar que el programa se ejecute como si todas las operaciones de memoria se realizaran en el orden especificado por el programador (orden del programa), por lo que las barreras de memoria no son necesarias. Sin embargo, cuando la memoria se comparte con varios dispositivos, como otras CPU en un sistema multiprocesador o periféricos asignados en memoria , el acceso desordenado puede afectar el comportamiento del programa. Por ejemplo, una segunda CPU puede ver los cambios de memoria realizados por la primera CPU en una secuencia que difiere del orden del programa.

Un programa se ejecuta a través de un proceso que puede tener múltiples subprocesos (es decir, un subproceso de software como pthreads en lugar de un subproceso de hardware). Diferentes procesos no comparten un espacio de memoria, por lo que esta discusión no se aplica a dos programas, cada uno ejecutándose en un proceso diferente (por lo tanto, un espacio de memoria diferente). Se aplica a dos o más subprocesos (de software) que se ejecutan en un único proceso (es decir, un único espacio de memoria donde varios subprocesos de software comparten un único espacio de memoria). Múltiples subprocesos de software, dentro de un solo proceso, pueden ejecutarse simultáneamente en un procesador multinúcleo .

El siguiente programa multiproceso, que se ejecuta en un procesador multinúcleo, ofrece un ejemplo de cómo dicha ejecución desordenada puede afectar el comportamiento del programa:

Inicialmente, las ubicaciones de memoria xy fambas contienen el valor 0. El hilo de software que se ejecuta en el procesador n.° 1 realiza un bucle mientras el valor de fes cero y luego imprime el valor de x. El hilo de software que se ejecuta en el procesador n.° 2 almacena el valor 42y xluego lo almacena 1en f. A continuación se muestra el pseudocódigo para los dos fragmentos del programa.

Los pasos del programa corresponden a instrucciones de procesadores individuales.

En el caso del procesador PowerPC, la eioioinstrucción asegura, como barrera de memoria, que cualquier operación de carga o almacenamiento iniciada previamente por el procesador se complete completamente con respecto a la memoria principal antes de que cualquier operación de carga o almacenamiento posterior iniciada por el procesador acceda a la memoria principal. memoria. [1] [2]

Hilo #1 Núcleo #1:

 mientras ( f == 0 ); // Aquí se requiere barrera de memoria print x ;      

Hilo #2 Núcleo #2:

 x = 42 ; // Aquí se requiere barrera de memoria f = 1 ;      

Se podría esperar que la declaración impresa imprima siempre el número "42"; sin embargo, si las operaciones de almacenamiento del subproceso n.° 2 se ejecutan fuera de orden, es posible que fse actualicen antes x y, por lo tanto, la declaración de impresión podría imprimir "0". De manera similar, las operaciones de carga del subproceso n.° 1 pueden ejecutarse fuera de orden y es posible xleerlas antes f de verificarlas y, nuevamente, la declaración de impresión podría imprimir un valor inesperado. Para la mayoría de los programas ninguna de estas situaciones es aceptable. Se debe insertar una barrera de memoria antes de la asignación del subproceso n.º 2 para fgarantizar que el nuevo valor de xsea visible para otros procesadores en el cambio del valor de , o antes f. Otro punto importante es que también se debe insertar una barrera de memoria antes del acceso del hilo n.° 1 para xgarantizar que el valor de xno se lea antes de ver el cambio en el valor de f.

Otro ejemplo es cuando un conductor realiza la siguiente secuencia:

 preparar datos para un módulo de hardware // La barrera de memoria requerida aquí activa el módulo de hardware para procesar los datos              

Si las operaciones de almacenamiento del procesador se ejecutan fuera de orden, el módulo de hardware puede activarse antes de que los datos estén listos en la memoria.

Para ver otro ejemplo ilustrativo (no trivial que surge en la práctica real), consulte bloqueo de doble verificación .

Programación multiproceso y visibilidad de la memoria.

Los programas multiproceso suelen utilizar primitivas de sincronización proporcionadas por un entorno de programación de alto nivel, como Java o .NET , o una interfaz de programación de aplicaciones (API), como POSIX Threads o Windows API . Se proporcionan primitivas de sincronización como mutexes y semáforos para sincronizar el acceso a recursos desde subprocesos de ejecución paralelos. Estas primitivas generalmente se implementan con las barreras de memoria necesarias para proporcionar la semántica de visibilidad de memoria esperada . En tales entornos, generalmente no es necesario el uso explícito de barreras de memoria.

Ejecución fuera de orden versus optimizaciones de reordenamiento del compilador

Las instrucciones de barrera de memoria abordan los efectos de reordenamiento sólo a nivel de hardware. Los compiladores también pueden reordenar las instrucciones como parte del proceso de optimización del programa . Aunque los efectos sobre el comportamiento del programa paralelo pueden ser similares en ambos casos, en general es necesario tomar medidas separadas para inhibir las optimizaciones de reordenamiento del compilador para datos que pueden ser compartidos por múltiples subprocesos de ejecución.

En C y C++ , la palabra clave volátil estaba destinada a permitir que los programas C y C++ accedan directamente a E/S asignadas en memoria. La E/S asignada en memoria generalmente requiere que las lecturas y escrituras especificadas en el código fuente se realicen en el orden exacto especificado, sin omisiones. Las omisiones o reordenamientos de lecturas y escrituras por parte del compilador romperían la comunicación entre el programa y el dispositivo al que se accede mediante E/S asignadas en memoria. El compilador de AC o C++ no puede omitir lecturas y escrituras en ubicaciones de memoria volátil, ni puede reordenar lecturas/escrituras en relación con otras acciones similares para la misma ubicación volátil (variable). La palabra clave volátil no garantiza una barrera de memoria para imponer la coherencia del caché. Por lo tanto, el uso de volátil por sí solo no es suficiente para utilizar una variable para la comunicación entre subprocesos en todos los sistemas y procesadores. [3]

Los estándares C y C++ anteriores a C11 y C++11 no abordan múltiples subprocesos (o múltiples procesadores) [4] y, como tal, la utilidad de volatile depende del compilador y el hardware. Aunque volátil garantiza que las lecturas y escrituras volátiles se producirán en el orden exacto especificado en el código fuente, el compilador puede generar código (o la CPU puede reordenar la ejecución) de modo que una lectura o escritura volátil se reordene con respecto a las no volátiles. -lecturas o escrituras volátiles, lo que limita su utilidad como indicador entre subprocesos o mutex.

Ver también

Referencias

  1. ^ Mayo, Cathy; Silha, Ed; Simpson, Eick; Warren, Hank (1993). La arquitectura PowerPC: una especificación para una nueva familia de procesadores RISC . Editores Morgan Kaufmann. pag. 350.ISBN​ 1-55860-316-6.
  2. ^ Kacmarcik, Cary (1995). Optimización del código PowerPC . Compañía editorial Addison-Wesley. pag. 188.ISBN 0-201-40839-2.
  3. ^ Corbet, Jonathan. "Por qué no se debe utilizar la clase de tipo 'volátil'". Kernel.org . Consultado el 13 de abril de 2023 .
  4. ^ Boehm, Hans (junio de 2005). Los subprocesos no se pueden implementar como biblioteca. Actas de la conferencia ACM SIGPLAN 2005 sobre diseño e implementación de lenguajes de programación . Asociación para Maquinaria de Computación . pag. 261. CiteSeerX 10.1.1.308.5939 . doi :10.1145/1065010.1065042. ISBN  1595930566.

enlaces externos