Trabajar en datos inmutables con asignaciones individuales tiene el efecto obvio de requerir más memoria, uno presumiría, porque constantemente está creando nuevos valores (aunque los compiladores debajo de las cubiertas hacen trucos de puntero para que esto sea menos problemático).
Pero he escuchado algunas veces que las pérdidas en el rendimiento son mayores que las ganancias en la forma en que la CPU (su controlador de memoria específicamente) puede aprovechar el hecho de que la memoria no está mutada (tanto).
Esperaba que alguien pudiera arrojar algo de luz sobre cómo esto es cierto (¿o si no lo es?).
En un comentario en otra publicación se mencionó que los tipos de datos abstractos (ADT) tienen que ver con esto, lo que me hizo aún más curioso, ¿cómo afectan específicamente los ADT la forma en que la CPU maneja la memoria? Sin embargo, esto es un aparte, principalmente estoy interesado en cómo la pureza del lenguaje necesariamente afecta el rendimiento de la CPU y sus cachés, etc.
fuente
let a = [1,2,3] in let b = 0:a in (a, b, (-1):c)
el intercambio reduce los requisitos de memoria, sino que depende de la definición de(:)
y[]
no el compilador. ¿Yo creo que? No estoy seguro de este.Respuestas:
La ventaja es que este hecho evita que el compilador use instrucciones membares cuando se accede a los datos.
Verá, cuando se accede a los datos desde diferentes subprocesos, en la CPU de varios núcleos se realiza de la siguiente manera: diferentes subprocesos se ejecutan en diferentes núcleos, cada uno con su propia caché (local a su núcleo), una copia de alguna caché global.
Si los datos son mutables y el programador necesita que sean consistentes entre diferentes hilos, se deben tomar medidas para garantizar la coherencia. Para el programador, esto significa usar construcciones de sincronización cuando acceden (por ejemplo, leen) a datos en un hilo particular.
Para el compilador, la construcción de sincronización en el código significa que necesita insertar una instrucción membar para asegurarse de que los cambios realizados en la copia de datos en uno de los núcleos se propaguen correctamente ("publicados"), para garantizar que los cachés en otros núcleos tener la misma copia (actualizada).
Algo más simple, vea la nota a continuación , esto es lo que sucede en el procesador multinúcleo para membar:
Verá, todos los núcleos no hacen nada mientras los datos se copian de un lado a otro entre cachés globales y locales . Esto es necesario para garantizar que los datos mutables estén correctamente sincronizados (seguro para subprocesos). Si hay 4 núcleos, los 4 se detienen y esperan mientras se sincronizan los cachés. Si hay 8, los 8 se detienen. Si hay 16 ... bueno, tienes 15 núcleos que no hacen exactamente nada mientras esperas las cosas que debes hacer en uno de estos.
Ahora, veamos qué sucede cuando los datos son inmutables. No importa a qué hilo acceda, se garantiza que sea el mismo. Para el programador, esto significa que no es necesario insertar construcciones de sincronización cuando acceden (leen) a los datos en un subproceso particular.
Para el compilador, esto a su vez significa que no es necesario insertar una instrucción membar .
Como resultado, el acceso a los datos no necesita detener los núcleos y esperar mientras los datos se escriben de un lado a otro entre cachés globales y locales. Esa es una ventaja del hecho de que la memoria no está mutada .
Tenga en cuenta que la explicación un poco simplificada anterior elimina algunos efectos negativos más complicados de que los datos sean mutables, por ejemplo, en la canalización . Para garantizar el pedido requerido, la CPU tiene que invalidar las líneas de pilotes afectadas por los cambios de datos; esa es otra penalización de rendimiento. Si esto se implementa mediante la invalidación directa (y por lo tanto confiable) de todas las tuberías, entonces el efecto negativo se amplifica aún más.
fuente