¿Por qué el uso de la misma línea de caché de varios subprocesos no causa una desaceleración grave?

Mira este fragmento:

#include <atomic>
#include <thread>

typedef volatile unsigned char Type;
// typedef std::atomic_uchar Type;

void fn(Type *p) {
    for (int i=0; i<500000000; i++) {
        (*p)++;
    }
}

int main() {
    const int N = 4;

    std::thread thr[N];
    alignas(64) Type buffer[N*64];

    for (int i=0; i<N; i++) {
        thr[i] = std::thread(&fn, &buffer[i*1]);
    }

    for (int i=0; i<N; i++) {
        thr[i].join();
    }

}

Este pequeño programa incrementa cuatro bytes adyacentes muchas veces desde cuatro subprocesos diferentes. Antes, usaba la regla: no use la misma línea de caché de diferentes subprocesos, ya que compartir líneas de caché es malo. Así que esperaba que una versión de cuatro hilos (N=4) es mucho más lento que una versión de un hilo (N=1)

Sin embargo, estas son mis medidas (en una CPU Haswell):

N = 1: 1 seg.N = 4: 1.2 segundos

EntoncesN=4 No es mucho más lento. Si uso diferentes líneas de caché (reemplazar*1 con*64), luegoN=4 se vuelve un poco más rápido: 1.1 seg.

Las mismas medidas para el acceso atómico (intercambie los comentarios entypedef), misma línea de caché:

N = 1: 3.1 segundosN = 4: 48 segundos

Entonces elN=4 El caso es mucho más lento (como esperaba). Si se utilizan diferentes líneas de caché, entoncesN=4 tiene un rendimiento similar alN=1: 3.3 seg.

No entiendo la razón detrás de estos resultados. ¿Por qué no consigo una desaceleración grave de lo no atómico,N=4 ¿caso? Cuatro núcleos tienen la misma memoria en sus cachés, por lo que deben sincronizarlos de alguna manera, ¿no? ¿Cómo pueden correr casi perfectamente paralelos? ¿Por qué solo el caso atómico se ralentiza gravemente?

Creo que necesito entender cómo se actualiza la memoria en este caso. Al principio, no hay núcleos tienenbuffer en sus escondites. Después de unafor iteración (enfn), los 4 núcleos tienenbuffer en sus líneas de caché, pero cada núcleo escribe un byte diferente. ¿Cómo se sincronizan estas líneas de caché (en el caso no atómico)? ¿Cómo sabe el caché, québyte ¿es sucio? ¿O hay algún otro mecanismo para manejar este caso? ¿Por qué este mecanismo es mucho más barato (en realidad, es casi gratis) que el atómico?

Respuestas a la pregunta(2)

Su respuesta a la pregunta