sirve - Múltiples hilos y caché de CPU
para que sirve la memoria cache (4)
En general, es una mala idea compartir regiones de memoria superpuestas como si un hilo procesa 0,2,4 ... y los otros procesos 1,3,5 ... Aunque algunas arquitecturas pueden admitir esto, la mayoría de las arquitecturas no lo hacen, y Probablemente no pueda especificar en qué máquinas se ejecutará su código. Además, el sistema operativo es libre de asignar su código a cualquier núcleo que le guste (uno solo, dos en el mismo procesador físico o dos núcleos en procesadores independientes). Además, cada CPU generalmente tiene un caché de primer nivel separado, incluso si está en el mismo procesador.
En la mayoría de las situaciones, 0,2,4 ... / 1,3,5 ... ralentizará el rendimiento hasta posiblemente ser más lento que una sola CPU. Herb Sutters "Eliminar Falso Compartir" demuestra esto muy bien.
El uso del esquema [... n / 2-1] y [n / 2 ... n] se escalará mucho mejor en la mayoría de los sistemas. Incluso puede llevar a un rendimiento súper lineal, ya que posiblemente se puede usar el tamaño de la memoria caché de todas las CPU. El número de subprocesos utilizados debe ser siempre configurable y debe tener como valor predeterminado el número de núcleos de procesador encontrados.
Estoy implementando una operación de filtrado de imágenes en C usando varios subprocesos y haciéndolo lo más optimizado posible. Sin embargo, tengo una pregunta: si se accede a la memoria mediante el subproceso 0, y al mismo tiempo si se accede a la misma memoria mediante el subproceso 1, ¿se obtendrá de la memoria caché? Esta pregunta surge de la posibilidad de que estos dos subprocesos puedan ejecutarse en dos núcleos diferentes de la CPU. Así que otra forma de poner esto es: ¿todos los núcleos comparten la misma memoria caché común?
Supongamos que tengo un diseño de memoria como el siguiente
salida int [100];
Supongamos que hay 2 núcleos de CPU y, por lo tanto, engendro dos subprocesos para que trabajen simultáneamente. Un esquema podría ser dividir la memoria en dos partes, 0-49 y 50-99 y dejar que cada hilo trabaje en cada parte. Otra forma podría ser permitir que el hilo-0 funcione en índices pares, como 0 2 4 y así sucesivamente .. mientras que el otro hilo funciona en índices impares como 1 3 5 ... Esta última técnica es más fácil de implementar (especialmente para 3D datos) pero no estoy seguro si podría usar el caché de manera eficiente de esta manera.
La respuesta a esta pregunta depende en gran medida de la arquitectura y el nivel de caché, junto con el lugar donde se ejecutan los subprocesos.
Por ejemplo, las CPU Intel multi core recientes tienen un caché L1 que es por núcleo y un caché L2 que se comparte entre los núcleos que están en el mismo paquete de CPU; Sin embargo, diferentes paquetes de CPU tendrán sus propios cachés L2.
Sin embargo, incluso en el caso de que los subprocesos se estén ejecutando en dos núcleos dentro del mismo paquete, si ambos subprocesos acceden a los datos dentro de la misma línea de caché, tendrá que rebotar entre los dos cachés L1. Esto es muy ineficiente, y debe diseñar su algoritmo para evitar esta situación.
Algunos comentarios han preguntado acerca de cómo evitar este problema.
En el fondo, realmente no es particularmente complicado: solo desea evitar que dos subprocesos intenten acceder simultáneamente a los datos que se encuentran en la misma línea de caché, donde al menos un subproceso está escribiendo en los datos. (Mientras todos los subprocesos solo estén leyendo los datos, no hay problema, en la mayoría de las arquitecturas, los datos de solo lectura pueden estar presentes en múltiples cachés).
Para hacer esto, necesita saber el tamaño de la línea de caché; esto varía según la arquitectura, pero actualmente la mayoría de los chips de familia x86 y x86-64 usan una línea de caché de 64 bytes (consulte el manual de arquitectura para otras arquitecturas). También necesitará saber el tamaño de sus estructuras de datos.
Si le pide a su compilador que alinee la estructura de datos compartidos de interés con un límite de 64 bytes (por ejemplo, su output
matriz), entonces sabe que comenzará al inicio de una línea de caché, y también puede calcular dónde Los límites de la línea de caché son. Si su int
es de 4 bytes, entonces cada cacheline contendrá exactamente 8 valores int
. Siempre que la matriz comience en un límite de línea de caché, entonces la output[0]
través de la output[7]
estará en una línea de caché, y la output[8]
través de la output[15]
en la siguiente. En este caso, diseñaría su algoritmo de modo que cada hilo funcione en un bloque de valores int
adyacentes que sea un múltiplo de 8.
Si está almacenando tipos de struct
complicados en lugar de int
simple, la utilidad pahole
será de utilidad. Analizará los tipos de struct
en su binario compilado y le mostrará el diseño (incluido el relleno) y el tamaño total. Luego puede ajustar sus struct
usando esta salida; por ejemplo, puede agregar manualmente algunos rellenos para que su struct
sea un múltiplo del tamaño de la línea de caché.
En los sistemas POSIX, la función posix_memalign()
es útil para asignar un bloque de memoria con una alineación específica.
Puede que esté confundido, pero si la memoria caché del núcleo está compartida o no depende de la implementación de la CPU. Tendría que buscar las hojas técnicas en la página del fabricante para verificar si cada núcleo en su CPU tiene su propio caché o si el caché fue compartido.
También estaba trabajando en la manipulación de imágenes para una empresa de seguridad y, a veces, conseguimos imágenes dañadas después de ejecutar operaciones por lotes en subprocesos. Después de largas investigaciones, llegamos a la conclusión de que la memoria caché se compartía entre los CPU Core y que, en raras ocasiones, los datos se sobrescribían o se reemplazaban por datos incorrectos.
Si esto es algo para tener en cuenta o es más bien un evento raro, no puedo responder.
Documentación de Intel
Intel publica hojas de datos por generación que pueden contener este tipo de información.
Por ejemplo, para el procesador i5-3210M que tenía en mi computadora más antigua, busco la 3ra generación - Hoja de datos Volumen 1 3.3 "Tecnología Intel Hyper-Threading (Tecnología Intel HT) dice:
El procesador es compatible con la tecnología Intel Hyper-Threading (tecnología Intel HT) que permite que un núcleo de ejecución funcione como dos procesadores lógicos. Si bien algunos recursos de ejecución, como cachés, unidades de ejecución y buses, se comparten, cada procesador lógico tiene su propio estado arquitectónico con su propio conjunto de registros de propósito general y registros de control.
lo que confirma que los cachés se comparten en un hipervínculo dado para esa generación de CPU.
Ver también:
- pregunta similar para compartir la memoria caché entre los núcleos: ¿Cómo se comparten las memorias caché en las CPU Intel de múltiples núcleos?
- análisis adicional de hilos vs núcleos: https://superuser.com/questions/133082/what-is-the-difference-between-hyper-threading-and-multiple-cores/995858#995858
- La especificación de la arquitectura en sí misma también tiene una sección sobre el uso compartido de ciertos recursos que debe ser válido en todas las implementaciones, aunque no menciona cachés: ¿cómo es el lenguaje de ensamblado multinúcleo?