CPU 的 Cache line 也可能成为限制多线程运行速度的一个因素

最近从好朋友那看到了两段有趣的代码

代码一:

#include <stdio.h>
#include <pthread.h>

struct {
    long a, b;
} s;

void* thread_1(void *x) {
    for (int i = 0; i < 99999999; ++i) {
        s.a += 1;
        s.b += 1;
    }
}

int main(void) {
    pthread_t t1;
    pthread_create(&t1, NULL, thread_1, NULL);
    pthread_join(t1, NULL);
    printf("%ld %ld\n", s.a, s.b);
    return 0;
}

代码二:

#include <stdio.h>
#include <pthread.h>

struct {
    long a, b;
} s;

void* thread_1(void *x) {
    for (int i = 0; i < 99999999; ++i) {
        s.a += 1;
    }
}

void* thread_2(void *x) {
    for (int i = 0; i < 99999999; ++i) {
        s.b += 1;
    }
}

int main(void) {
    pthread_t t1, t2;
    pthread_create(&t1, NULL, thread_1, NULL);
    pthread_create(&t2, NULL, thread_2, NULL);
    pthread_join(t1, NULL);
    pthread_join(t2, NULL);
    printf("%ld %ld\n", s.a, s.b);
    return 0;
}

双线程对战单线程,结果如何?

乍一看,多线程版本的代码可以更高效地利用 CPU,每个线程操作的是不同的字段,也不存在临界区,应该就是它更快了吧!

可是,实际运行一下,结果却是:

img

单线程更快,怎么回事呢?原来是CPU缓存的问题!现代多核CPU通常具有多级缓存,每个核都有自己的缓存。当多个线程同时访问共享的数据时,如果这些数据不在缓存中,就会导致缓存一致性的问题,需要不断地将数据从主内存加载到缓存中,以确保各个核之间的数据一致性。结果,运行速度就不如单线程快了。

如果想要解决,或许可以试试让它们不在一个 Cache line 中,避免缓存一致性的问题。

多线程,很神奇吧!