¿Qué hace el procesador mientras espera una recuperación de memoria principal?


26

Suponiendo que las solicitudes de caché l1 y l2 resultan en una falla, ¿se detiene el procesador hasta que se haya accedido a la memoria principal?

Escuché sobre la idea de cambiar a otro hilo, si es así, ¿qué se usa para despertar el hilo estancado?


44
¿Qué investigación has hecho? Esta es ciertamente la información que está disponible. Dejaré las respuestas a los expertos, pero no creo que un cambio de hilo sea algo útil. En general, el cambio de contexto en una CPU inducirá muchos accesos a la memoria (y, por lo tanto, probablemente se pierda la memoria caché). Existen algunas medidas, como la reordenación de la operación (utilizando la tubería), pero el estancamiento parece no tener otra alternativa.
Raphael

@Raphael Leí principalmente libros de arquitectura de computadoras, ARM System-on-Chip Architecture de Steve Furber, fue probablemente el más completo que he leído por completo. Sin embargo, he comenzado a leer Computer Architecture: A Quantitative Approach. Discute técnicas para evitar el estancamiento, como el cambio de hilos, OOE y operaciones de memoria fuera de orden, aunque en realidad nunca da mucho sobre las complejidades de los diseños modernos, ya que como la mayoría de los libros de texto cubren arquitecturas antiguas o dan sugerencias vagas sobre cómo son estas cosas. implementado y trabajar juntos.
102948239408

Ampliando mi pregunta, los cachés parecen tener latencias más pequeñas y ser deterministas en su respuesta, pero en caso de tener un recorrido de la tabla de la página del peor de los casos para recuperar la dirección física, se podrían completar miles de instrucciones, algunas del mismo hilo extraído por ILP. Qué interacciones de hardware se producen en el procesador para decidir que puede programar otro subproceso y qué comunicación se utiliza para activar ese subproceso si esto sucede. Aún más, si OoOE, ¿hay alguna técnica para lidiar con una cola de resultados completa al cambiar los hilos?
102948239408

1
No queda claro a partir de su pregunta que le interesen los detalles de las CPU modernas. Probablemente, esto no solo sea tópico, sino que también podría ser información patentada. Con los conceptos, podemos ayudarlo; Estos probablemente han cambiado menos a lo largo de las décadas que las implementaciones. En cuanto a su pregunta, incorpore lo que sabe y formule una pregunta específica, conceptual (o solicitud de referencia).
Raphael

1
He respondido acerca de los conceptos generales, pero a juzgar por sus comentarios, es posible que busque consideraciones más avanzadas. Sin embargo, si desea respuestas más avanzadas, deberá hacer que su pregunta sea más específica para arquitecturas particulares y tipos de técnicas.
Gilles 'SO- deja de ser malvado'

Respuestas:


28

La latencia de la memoria es uno de los problemas fundamentales estudiados en la investigación de arquitectura de computadoras.

Ejecución especulativa

La ejecución especulativa con un problema de instrucción fuera de orden a menudo es capaz de encontrar trabajo útil para completar la latencia durante un hit de caché L1, pero generalmente se queda sin trabajo útil después de 10 o 20 ciclos más o menos. Ha habido varios intentos de aumentar la cantidad de trabajo que se puede hacer durante una falta de latencia larga. Una idea era intentar hacer predicciones de valor (Lipasti, Wilkerson y Shen, (ASPLOS-VII): 138-147, 1996). Esta idea estuvo muy de moda en los círculos de investigación de arquitectura académica durante un tiempo, pero parece no funcionar en la práctica. Un último intento de salvar la predicción del valor del basurero de la historia fue la ejecución runahead(Mutlu, Stark, Wilkerson y Patt (HPCA-9): 129, 2003). En la ejecución de runahead, reconoce que sus predicciones de valor van a estar equivocadas, pero ejecute de forma especulativa de todos modos y luego descarte todo el trabajo basado en la predicción, en la teoría de que al menos comenzará algunas captaciones previas para lo que de otro modo sería caché L2 echa de menos Resulta que el runahead desperdicia tanta energía que simplemente no vale la pena.

Un enfoque final en este sentido, que puede estar obteniendo cierta tracción en la industria, implica crear buffers de pedidos enormemente largos. Las instrucciones se ejecutan especulativamente en función de la predicción de rama, pero no se realiza ninguna predicción de valor. En cambio, todas las instrucciones que dependen de una carga de latencia larga omiten sentarse y esperar en el búfer de reordenamiento. Pero dado que el búfer de reordenamiento es tan grande que puede seguir buscando instrucciones si el predictor de bifurcación está haciendo un trabajo decente, a veces podrá encontrar trabajo útil mucho más tarde en la secuencia de instrucciones. Un artículo de investigación influyente en esta área fueron las tuberías de flujo continuo(Srinivasan, Rajwar, Akkary, Gandhi y Upton (ASPLOS-XI): 107-119, 2004). (A pesar del hecho de que todos los autores son de Intel, creo que la idea obtuvo más tracción en AMD).

Multihilo

El uso de múltiples subprocesos para la tolerancia de latencia tiene una historia mucho más larga, con un éxito mucho mayor en la industria. Todas las versiones exitosas utilizan soporte de hardware para subprocesos múltiples. La versión más simple (y más exitosa) de esto es lo que a menudo se llama FGMT (multihilo de grano fino ) o multihilo intercalado . Cada núcleo de hardware admite contextos de subprocesos múltiples (un contexto es esencialmente el estado del registro, incluidos los registros como el puntero de instrucción y cualquier registro de banderas implícito). En un procesador multihilo de grano fino, cada hilo se procesa en-orden. El procesador realiza un seguimiento de qué subprocesos están detenidos en un error de carga de latencia larga y cuáles están listos para su próxima instrucción y utiliza una estrategia de programación FIFO simple en cada ciclo para elegir qué subproceso listo para ejecutar ese ciclo. Un primer ejemplo de esto a gran escala fueron los procesadores HEP de Burton Smith (Burton Smith diseñó la supercomputadora Tera, que también era un procesador multihilo de grano fino). Pero la idea va mucho más atrás, en la década de 1960, creo.

FGMT es particularmente efectivo en las cargas de trabajo de transmisión. Todas las GPU modernas (unidades de procesamiento de gráficos) son multinúcleo donde cada núcleo es FGMT, y el concepto también se usa ampliamente en otros dominios informáticos. El T1 de Sun también era FMGT multinúcleo, y también lo es el Xeon Phi de Intel (el procesador que a menudo todavía se llama "MIC" y solía llamarse "Larabee").

La idea de subprocesamiento múltiple simultáneo (Tullsen, Eggers y Levy, (ISCA-22): 392-403, 1995) combina el subprocesamiento múltiple de hardware con la ejecución especulativa. El procesador tiene múltiples contextos de subprocesos, pero cada subproceso se ejecuta de forma especulativa y fuera de orden. Un programador más sofisticado puede usar varias heurísticas para obtener el hilo que probablemente tenga un trabajo útil ( Malik, Agarwal, Dhar y Frank, (HPCA-14: 50-61), 2008 ). Cierta gran compañía de semiconductores comenzó a usar el término hyperthreading para multithreading simultáneo, y ese nombre parece ser el más utilizado en estos días.

Problemas de microarquitectura de bajo nivel

Después de releer sus comentarios, me di cuenta de que también está interesado en la señalización que se produce entre el procesador y la memoria. Los cachés modernos generalmente permiten que múltiples fallas se destaquen simultáneamente. Esto se llama caché sin bloqueo (Kroft, (ISCA-8): 81-87, 1981). (Pero el documento es difícil de encontrar en línea, y algo difícil de leer. Respuesta breve: hay mucha contabilidad, pero solo hay que lidiar con ella. La estructura de contabilidad de hardware se llama MSHR (registro de retención de información / estado de falta) ), que es el nombre que Kroft le dio en su artículo de 1981).


Gracias respuesta realmente completa, voy a tratar de buscar en el caché sin bloqueo. Mi pregunta mal formulada realmente buscaba confirmar que los procesadores continuaron con cargas y almacenes durante un acceso a la memoria principal y qué técnicas de microarquitectura se usaron para hacer esto.
102948239408

+1, 1. ¿Realmente es procesamiento de barril si no se utiliza la programación de turnos rotativos? Wikipedia lo convierte en sinónimo de FGMT. (Puedo aceptar la aplicación de "procesador de barril" al round robin con salto, aunque eso rompe la analogía ya que una duela faltante (cf. hilo no preparado) no contrae la circunferencia de un barril. (Creo que los procesadores de barril "verdaderos" eran ? quizá rara-procesador periférico para el CDC 6600 -porque desperdician un ciclo pero lo hace de hardware simplificar) 2. una mención de SoEMT como de Itanium Hyper-Threading y Northstar de IBM y otros parece especialmente apropiado dada la pregunta...
Paul A. Clayton

@ 102948239408, otra cosa que podría buscar en Google son términos como "hit under miss" y "miss under miss" (la otra opción es "stall under miss", pero acabo de probarlo y parece que no devuelve nada útil). términos que actualmente utilizan (algunos) arquitectos para diferentes opciones de lo que la caché podría permitir.
Wandering Logic

@ PaulA.Clayton, la terminología definitivamente no es mi fuerte. Estoy de acuerdo con usted en que el procesamiento del barril debería significar round-robin. Pero no puedo pensar en ningún otro término que signifique: entrelazado ciclo por ciclo de un montón de subprocesos en orden (que es lo que hacen las GPU, Xeon Phi y Sun T1). ¿Es FGMT? Siempre pensé que FGMT incluía SMT (es decir, no especifica que los subprocesos deben ejecutarse en orden), pero ¿tal vez FGMT es mejor que el "procesador barril" para este caso?
Wandering Logic

El artículo del procesador Barrel de Wikipedia dice: "también conocido como" intercalado "o" multihilo temporal "de grano fino", por lo que IMT y FGMT son al menos términos reconocidos. Creo que he leído "grano fino" más que "intercalado", pero no es raro que sea intercalado. En general, he usado FG (para mí "granulado" implica más separación de la que proporciona SMT); FG tiene la ventaja de que el intercalado podría aplicarse a SoEMT. Sospecho que esto es solo un cambio en el uso del "procesador de barril" que tendré que sonreír (mis dientes) y soportar.
Paul A. Clayton

16

La respuesta corta es: nada, el procesador se detiene.

No hay tantas posibilidades. Cambiar a una tarea diferente no es realmente una opción por dos razones. Esa es una operación costosa, y dado que la tarea actual y otras tareas compiten por el espacio en el caché, cambiar a la otra tarea puede requerir un acceso a la memoria principal, y también puede volver a la tarea original. Además, esto tendría que involucrar al sistema operativo, por lo que el procesador tendría que activar algún tipo de interrupción o trampa ; de hecho, el procesador estaría cambiando a algún código del núcleo.

Mientras el procesador está parado, el temporizador continúa funcionando, por lo que podría haber una interrupción del temporizador o podría haber una interrupción de otros periféricos. Por lo tanto, es más probable que ocurra un cambio de contexto durante un acceso a la memoria principal que durante un acceso a la memoria caché, pero solo porque lleva más tiempo.

No obstante, las computadoras modernas sí incluyen una variedad de técnicas para tratar de reducir el tiempo perdido en el procesador esperando la memoria principal. El estancamiento ocurre, pero solo cuando no se puede evitar.

Una técnica son las recuperaciones especulativas : el procesador intenta adivinar a qué ubicación de memoria se accederá y la recupera en caché antes de tiempo. Por ejemplo, los bucles sobre un bloque de memoria son comunes, por lo que si se han cargado líneas de caché para las direcciones de memoria 0x12340000, 0x12340010 y 0x12340020, puede ser una buena idea cargar la línea para 0x12340030. El compilador puede ayudar generando instrucciones de captación previa que son como cargas, excepto que solo transfieren datos de la memoria principal al caché, no a un registro del procesador.

Otra técnica es la ejecución especulativa . El procesador comienza a ejecutar la siguiente instrucción antes de realizar la carga. Esto sucede naturalmente de todos modos debido a la canalización de instrucciones. Solo las instrucciones que no dependen del valor cargado se pueden ejecutar de esta manera: el procesador debe realizar un análisis de dependencia. Para instrucciones condicionales (por ejemplo, carga r1; bifurcación si r1 ≠ 0), los procesadores emplean heurísticas de predicción de bifurcación para adivinar cuál será el valor. La ejecución especulativa después de una carga puede necesitar ser rebobinada en caso de que la carga desencadene un aborto.

Algunas arquitecturas como Itanium facilitan la ejecución de instrucciones en un orden conveniente al permitir el reordenamiento de instrucciones por defecto: en lugar de consistir en una secuencia de instrucciones elementales que se ejecutan semánticamente una tras otra, los programas consisten en palabras de instrucción muy largas : una sola instrucción incluye muchas operaciones que deben ser ejecutadas en paralelo por diferentes componentes del procesador.

El cambio a otro subproceso ocurre en hyperthreading , que se encuentra en procesadores x86 de gama alta. Esta es una técnica de diseño de hardware: cada núcleo de procesador contiene dos bancos de registros separados (cada uno correspondiente a un contexto de tarea), pero una sola instancia de otros elementos, de modo que puede admitir dos subprocesos de ejecución independientes, pero solo ejecuta de manera efectiva las instrucciones de uno a un momento. Mientras un hilo está parado, el otro hilo continúa. Desde el punto de vista del software, hay dos procesadores independientes; Simplemente sucede que esos procesadores comparten muchos componentes debajo del capó.

El intercambio es un nivel más en la jerarquía de memoria caché: la memoria principal puede verse como una memoria caché para el espacio de intercambio. Con el intercambio, los mecanismos y las relaciones de rendimiento son diferentes. Si una tarea necesita que se carguen datos del intercambio, la instrucción de carga activa una trampa que ejecuta el código del núcleo para asignar una página en la RAM y cargar su contenido desde el disco. Mientras esto sucede, el núcleo puede decidir cambiar a otra tarea.


Contrastando el primer y el penúltimo párrafo, el "truco" es que no es necesario un cambio de contexto real con hyperthreading, ¿verdad? La CPU mantiene completamente dos contextos al mismo tiempo.
Raphael

1
@Raphael Right: en lo que respecta al software, para todo menos el rendimiento, hay dos CPU.
Gilles 'SO- deja de ser malvado'

Una CPU hyperthreaded tiene muchas unidades de ejecución semi-independientes (sumadores enteros y de coma flotante, multiplicadores, etc.), y creo que ambos contextos pueden usar unidades de ejecución separadas al mismo tiempo, aunque no estoy 100% seguro de esto.
Russell Borogove

@RussellBorogove Sí, yo no lo mencionó porque incluso las CPU no hyperthreaded pueden tener múltiples ALU / FPU / ... y por el contrario núcleos separados a veces comparten FPU etc.
Gilles 'SO- estar parada mal'

5

La respuesta a esta pregunta variará con la arquitectura en cuestión. Si bien muchas CPU se detendrán (ARM, x86 sin hyperthreading, etc.) porque les lleva demasiado tiempo cambiar los hilos, ese no es el enfoque adoptado por cada arquitectura. En algunas arquitecturas, cada subproceso programado en una CPU tiene su propio archivo de registro independiente, por lo que el procesador simplemente puede ejecutar el trabajo desde un subproceso que no está esperando un acceso a la memoria. Tengo entendido que esto es, hasta cierto punto, lo que hace x86 hyperthreading (usando solo 2 hilos), pero es mucho más común en GPGPUarquitecturas En el caso particular de CUDA, al menos docenas, si no cientos, de urdimbres de hilos generalmente se cargan en un multiprocesador dado en un momento dado, con cada hilo (cientos o miles de ellos) tienen sus propios registros. Esto permite que la arquitectura ejecute una instrucción desde otro subproceso en el siguiente ciclo cuando un subproceso determinado emite un acceso a la memoria. Por lo tanto, siempre que se carguen suficientes subprocesos, los núcleos del procesador nunca estarán inactivos para acceder a la memoria. Consulte las Pautas de rendimiento y la Jerarquía de memoria para obtener más información.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.