stringtranslate.com

Falso compartir

En informática , el intercambio falso es un patrón de uso que degrada el rendimiento y que puede surgir en sistemas con cachés coherentes y distribuidos del tamaño del bloque de recursos más pequeño administrado por el mecanismo de almacenamiento en caché. Cuando un participante del sistema intenta acceder periódicamente a datos que no están siendo alterados por otra parte, pero esos datos comparten un bloque de caché con los datos que están siendo alterados, el protocolo de almacenamiento en caché puede obligar al primer participante a recargar todo el bloque de caché a pesar de la falta de necesidad lógica. [1] El sistema de almacenamiento en caché desconoce la actividad dentro de este bloque y obliga al primer participante a soportar la sobrecarga del sistema de almacenamiento en caché requerida por el verdadero acceso compartido de un recurso.

Cachés de CPU multiprocesador

Con diferencia, el uso más común de este término es en las cachés de CPU de multiprocesadores modernos , donde la memoria se almacena en caché en líneas de una potencia pequeña de dos palabras (por ejemplo, 64 bytes contiguos alineados ). Si dos procesadores operan con datos independientes en la misma región de direcciones de memoria almacenables en una sola línea, los mecanismos de coherencia de la caché en el sistema pueden forzar toda la línea a través del bus o interconectarse con cada escritura de datos, lo que forzará paradas de memoria además de desperdiciar el ancho de banda del sistema. . En algunos casos, la eliminación del intercambio falso puede dar lugar a mejoras de rendimiento de gran magnitud. [2] El uso compartido falso es un artefacto inherente a los protocolos de caché sincronizados automáticamente y también puede existir en entornos como sistemas de archivos distribuidos o bases de datos, pero la prevalencia actual se limita a los cachés de RAM.

Ejemplo

#include <iostream> #include <thread> #include <new> #include <atomic> #include <chrono> #include <latch> #include <vector>       usando el espacio de nombres estándar ; usando el espacio de nombres crono ;    #if definido (__cpp_lib_hardware_interference_size) // tamaño de línea de caché predeterminado desde el tiempo de ejecución constexpr size_t CL_SIZE = hardware_constructivo_interference_size ; #else // tamaño de línea de caché más común; de lo contrario, constexpr size_t CL_SIZE = 64 ; #terminara si        int main () { vector < jthread > hilos ; int hc = jthread :: hardware_concurrency (); hc = hc <= CL_SIZE ? hc : CL_SIZE ; for ( int nThreads = 1 ; nThreads <= hc ; ++ nThreads ) { // sincronizar el comienzo de los subprocesos de forma aproximada en el pestillo del nivel del kernel coarseSync ( nThreads ); // sincronización fina mediante atomic en el espacio de usuario atomic_uint fineSync ( nThreads ); // tantos caracteres como cabrían en una línea de caché struct alignas ( CL_SIZE ) { charshares [ CL_SIZE ] ; } línea de caché ; // suma de los tiempos de ejecución de todos los subprocesos atomic_int64_t nsSum ( 0 ); para ( int t = 0 ; t ! = nThreads ; ++ t ) subprocesos . emplace_back ( [ & ]( char volatile & c ) { coarseSync . Arrival_and_wait (); // sincroniza el comienzo de la ejecución del hilo a nivel de kernel if ( fineSync . fetch_sub ( 1 , Memory_order :: Relaxed ) != 1 ) // fine- sincronizar a nivel de usuario mientras ( fineSync . cargar ( memoria_orden :: relajado ) ); inicio automático = reloj_alta_resolución :: ahora (); para ( tamaño_t r = 10'000'000 ; r - ; ) c = c + 1 ; nsSum += duración_cast < nanosegundos > (                                                                                                       reloj_alta_resolución :: ahora () - inicio ). contar (); }, ref ( cacheLine . compartidos [ t ] ) ); hilos . cambiar el tamaño ( 0 ); // unir todos los hilos cout << nThreads << ": " << ( int )( nsSum / ( 1.0e7 * nThreads ) + 0.5 ) << endl ; } }                            

Este código muestra el efecto del intercambio falso. Crea una cantidad cada vez mayor de subprocesos desde un subproceso hasta la cantidad de subprocesos físicos en el sistema. Cada subproceso incrementa secuencialmente un byte de una línea de caché, que en su conjunto se comparte entre todos los subprocesos. Cuanto mayor sea el nivel de contención entre subprocesos, más tiempo llevará cada incremento. Estos son los resultados en un sistema Zen4 con 16 núcleos y 32 hilos:

1: 12: 43: 64: 95: 116: 137: 158: 179: 1610: 1811: 2112: 2513: 2914: 3515: 3916: 4117: 4318:4419: 4820: 4921: 5122: 5323: 5824: 6125:6826: 7527: 7928: 8229: 8530: 8831: 9132: 94

Como puede ver, en el sistema en cuestión puede tardar hasta 100 nanosegundos en completar una operación de incremento en la línea de caché compartida, lo que corresponde a aprox. 420 ciclos de reloj en esta CPU.

Mitigación

Hay formas de mitigar los efectos del intercambio falso. Por ejemplo, el intercambio falso en las cachés de la CPU se puede evitar reordenando las variables o agregando relleno (bytes no utilizados) entre las variables. Sin embargo, algunos de estos cambios de programa pueden aumentar el tamaño de los objetos, lo que lleva a un mayor uso de memoria. [2] Las transformaciones de datos en tiempo de compilación también pueden mitigar el intercambio falso. [3] Sin embargo, es posible que algunas de estas transformaciones no siempre estén permitidas. Por ejemplo, el borrador estándar del lenguaje de programación C++ de C++ 23 exige que los miembros de datos deben distribuirse de manera que los miembros posteriores tengan direcciones más altas. [4]

Existen herramientas para detectar intercambios falsos. [5] [6] También existen sistemas que detectan y reparan el intercambio falso en programas en ejecución. Sin embargo, estos sistemas incurren en algunos gastos generales de ejecución. [7] [8]

Referencias

  1. ^ Patterson, David (2012). Organización y diseño de ordenadores: la interfaz hardware/software . Waltham, MA: Morgan Kaufmann. pag. 537.ISBN _ 978-0-12-374750-1. OCLC  746618653.
  2. ^ ab Bolosky, William J.; Scott, Michael L. (22 de septiembre de 1993). "Falso intercambio y su efecto en el rendimiento de la memoria compartida". Sedms'93: Sistemas USENIX sobre experiencias USENIX con sistemas distribuidos y multiprocesadores . 4 . Consultado el 11 de julio de 2021 .
  3. ^ Jeremiassen, Tor E.; Eggers, Susan J. (1995). "Reducir el intercambio falso en multiprocesadores de memoria compartida mediante transformaciones de datos en tiempo de compilación". Avisos ACM SIGPLAN . Asociación de Maquinaria de Computación (ACM). 30 (8): 179–188. doi : 10.1145/209937.209955 . ISSN  0362-1340.
  4. ^ "Borrador de trabajo, estándar para el lenguaje de programación C++ [clase]". anguila.es . Consultado el 11 de julio de 2021 .
  5. ^ "perf-c2c (1)". Página del manual de Linux . 2016-09-01 . Consultado el 8 de agosto de 2021 .
  6. ^ Chabbi, Milind; Wen, Shasha; Liu, Xu (10 de febrero de 2018). "Detección de intercambio falso sobre la marcha de Featherlight". Actas del 23º Simposio ACM SIGPLAN sobre principios y práctica de la programación paralela . Nueva York, NY, Estados Unidos: ACM. págs. 152-167. doi :10.1145/3178487.3178499. ISBN 9781450349826.
  7. ^ Nanavati, Mihir; Lanza, Marcos; Taylor, Natán; Rajagopalan, Shriram; Meyer, holandés T.; Aiello, William; Warfield, Andrés (2013). "¿De quién es la línea de caché?". Actas de la octava conferencia europea ACM sobre sistemas informáticos . Nueva York, Nueva York, Estados Unidos: ACM Press. págs. 141-154. doi :10.1145/2465351.2465366. ISBN 9781450319942.
  8. ^ Liu, Tongping; Berger, Emery D. (18 de octubre de 2011). "SHERIFF: detección precisa y mitigación automática del intercambio falso". Avisos ACM SIGPLAN . Asociación de Maquinaria de Computación (ACM). 46 (10): 3–18. doi :10.1145/2076021.2048070. ISSN  0362-1340.

enlaces externos