缓存行和伪共享

cpu使用缓存以提升读取数据速度,缓存使用缓存行的形式,每次从主存load64字节到缓存。在多核架构中, 会发生一种伪共享的情况,原因是,每个cpu缓存自己的数据,cpu0对long i进行操作,缓存了i和i附近共计64字节数据;cpu1对long j进行操作,缓存了j和j附近共计64字节的数据。若i和j相邻,那么两个cpu缓存的就是主存中相同的一段64字节的内存数据。此时若cpu0对i的操作是写,导致这段地址上的数据对于其他cpu的缓存失效,cpu1尽管不操作i,也需要重新去主存中load这段数据,导致了额外的开销,验证代码如下:

struct Test {
    volatile long i;
    volatile long j;
};

struct Test t;

void* thread_func1(void* arg) {
    long i;
    for (i = 0; i < 100000000; i++) {
        t.i++;
    }
}

void* thread_func2(void* arg) {
    long i;
    for (i = 0; i < 100000000; i++) {
        t.j++;
    }
}

void func() {
    pthread_t pid[2];
    pthread_create(&pid[0], NULL, thread_func1, NULL);
    pthread_create(&pid[1], NULL, thread_func2, NULL);
    pthread_join(pid[0], NULL);
    pthread_join(pid[1], NULL);
}

代码解释:定义一个struct Test,其中i和j都是long(8字节)并相邻,这样就会出现在cpu的同一个缓存行中,声明一个全局的Test实例t用于操作。注意i和j都要是volatile的,保证写回内存,防止优化导致使用寄存器。开两个线程,分别操作i和j100M次,制造一个交叉执行的情况。测试程序执行时间,如下:

改造Test内部结构,在i和j之间填充7个long,保证i和j不会出现在同一个缓存行中,改造后的Test结构如下,程序其他部分不变:

struct Test {
    volatile long i;
    long p1, p2, p3, p4, p5, p6, p7;
    volatile long j;
};

再次测试时间如下:

可以看到,程序执行时间显著减少,从而证明了伪共享的存在和解决的方案

你可能感兴趣的:(linux)