programacion memoria lenguaje ejemplos dinamica dev comandos basicos c++ multithreading thread-safety

c++ - lenguaje - memoria dinamica en c



¿Qué hace el compilador de C++ para garantizar que las ubicaciones de memoria diferentes pero adyacentes sean seguras para usarse en diferentes hilos? (2)

Como han explicado otros, nada en particular en hardware común. Sin embargo, hay un retén: el compilador debe abstenerse de realizar ciertas optimizaciones, a menos que pueda probar que otros subprocesos no acceden a las ubicaciones de memoria en cuestión, por ejemplo:

std::array<std::uint8_t, 8u> c; void f() { c[0] ^= 0xfa; c[3] ^= 0x10; c[6] ^= 0x8b; c[7] ^= 0x92; }

Aquí, en un modelo de memoria de un solo hilo, el compilador podría emitir código como el siguiente (pseudoensamblaje; asume hardware little-endian):

load r0, *(std::uint64_t *) &c[0] xor r0, 0x928b0000100000fa store r0, *(std::uint64_t *) &c[0]

Es probable que esto sea más rápido en el hardware común que en el caso de los bytes individuales. Sin embargo, lee y escribe los elementos no afectados (y no mencionados) de c en los índices 1, 2, 4 y 5. Si otros subprocesos escriben en estas ubicaciones de memoria al mismo tiempo, estos cambios podrían sobrescribirse.

Por esta razón, las optimizaciones como estas a menudo son inutilizables en un modelo de memoria multihebra. Siempre que el compilador realice solo cargas y almacenes de la misma longitud, o fusione los accesos solo cuando no haya espacio (por ejemplo, los accesos a c[6] c[7] todavía pueden fusionarse), el hardware generalmente ya proporciona la información necesaria. Garantías para la correcta ejecución.

(Dicho esto, hay / ha habido algunas arquitecturas con garantías de orden de memoria débiles y contraintuitivas, por ejemplo, DEC Alpha no rastrea los punteros como una dependencia de datos de la forma en que lo hacen otras arquitecturas, por lo que es necesario introducir una barrera de memoria explícita en algunos casos, en código de bajo nivel. Hay una pequeña perorata algo conocida por Linus Torvalds sobre este problema . Sin embargo, se espera que una implementación de C ++ conforme lo proteja de tales problemas.)

Digamos que tengo una estructura:

struct Foo { char a; // read and written to by thread 1 only char b; // read and written to by thread 2 only };

Ahora, por lo que entiendo, el estándar C ++ garantiza la seguridad de lo anterior cuando dos subprocesos operan en las dos ubicaciones de memoria diferentes.

Sin embargo, creo que, dado que char a y char b, caen dentro de la misma línea de caché, el compilador tiene que hacer una sincronización extra.

¿Qué sucede exactamente aquí?


Esto depende del hardware. En el hardware con el que estoy familiarizado, C ++ no tiene que hacer nada especial, ya que desde la perspectiva del hardware, el acceso a diferentes bytes, incluso en una línea en caché, se maneja de manera "transparente". Desde el hardware, esta situación no es realmente diferente de

char a[2]; // or char a, b;

En los casos anteriores, estamos hablando de dos objetos adyacentes, que se garantiza que son accesibles de forma independiente.

Sin embargo, he puesto ''transparentemente'' entre comillas por una razón. Cuando realmente tiene un caso como ese, podría estar sufriendo (en lo que respecta al rendimiento) de un "intercambio falso", lo que sucede cuando dos (o más) subprocesos acceden a la memoria adyacente simultáneamente y terminan almacenados en caché en varios cachés de la CPU. Esto lleva a la invalidación constante de caché. En la vida real, se debe tener cuidado para evitar que esto suceda cuando sea posible.