多线程中的虚假共享
False sharing in multi threads
随着我增加 NTHREADS,以下代码 运行 变慢了。为什么使用更多线程会使程序 运行 变慢?有什么办法可以解决吗?有人说这是关于虚假分享,但我不太明白这个概念。
程序基本上计算1到100000000的和。使用多线程的想法是将数字列表分成几个chunck,并并行计算每个chunck的和,以加快计算速度。
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#define LENGTH 100000000
#define NTHREADS 2
#define NREPEATS 10
#define CHUNCK (LENGTH / NTHREADS)
typedef struct {
size_t id;
long *array;
long result;
} worker_args;
void *worker(void *args) {
worker_args *wargs = (worker_args*) args;
const size_t start = wargs->id * CHUNCK;
const size_t end = wargs->id == NTHREADS - 1 ? LENGTH : (wargs->id+1) * CHUNCK;
for (size_t i = start; i < end; ++i) {
wargs->result += wargs->array[i];
}
return NULL;
}
int main(void) {
long* numbers = malloc(sizeof(long) * LENGTH);
for (size_t i = 0; i < LENGTH; ++i) {
numbers[i] = i + 1;
}
worker_args *args = malloc(sizeof(worker_args) * NTHREADS);
for (size_t i = 0; i < NTHREADS; ++i) {
args[i] = (worker_args) {
.id = i,
.array = numbers,
.result = 0
};
}
pthread_t thread_ids[NTHREADS];
for (size_t i = 0; i < NTHREADS; ++i) {
pthread_create(thread_ids+i, NULL, worker, args+i);
}
for (size_t i = 0; i < NTHREADS; ++i) {
pthread_join(thread_ids[i], NULL);
}
long sum = 0;
for (size_t i = 0; i < NTHREADS; ++i) {
sum += args[i].result;
}
printf("Run %2zu: total sum is %ld\n", n, sum);
free(args);
free(numbers);
}
Why use more threads make the program run slower?
创建和加入线程会产生开销。如果线程无事可做,那么这种开销可能比实际工作更昂贵。
你的线程只是在做一个简单的求和,并不那么昂贵。还要考虑从例如10 到 11 个线程不会改变每个线程的工作负载。
10 个线程 --> 每个线程 10000000 个总和
11 个线程 --> 每个线程 9090909 个总和
创建额外线程的开销可能超过每个线程 "work load saved"。
在我的 PC 上,程序运行时间不到 100 毫秒。多线程不值得麻烦。
在多线程值得做之前,您需要处理更密集的任务。
另请注意,创建多于计算机内核数(包括超线程)的线程很少有意义。
false sharing
是的,"false sharing" 会影响多线程程序的性能,但我怀疑这是您遇到的真正问题。
"false sharing" 是在(某些)缓存系统中发生的事情,当两个线程(或者更确切地说是两个内核)写入属于同一缓存行的两个不同变量时。在这种情况下,两个 threads/cores 竞争拥有缓存行(用于写入),因此,他们将不得不一次又一次地刷新内存和缓存。这对性能不利。
正如我所说 - 我怀疑这是你的问题。一个聪明的编译器会让你的循环只使用 CPU 寄存器并且只在最后写入内存。您可以检查代码的反汇编,看看是否属于这种情况。
您可以通过增加结构的 sizeof 来避免 "false sharing",这样每个结构都适合您系统上缓存行的大小。
随着我增加 NTHREADS,以下代码 运行 变慢了。为什么使用更多线程会使程序 运行 变慢?有什么办法可以解决吗?有人说这是关于虚假分享,但我不太明白这个概念。
程序基本上计算1到100000000的和。使用多线程的想法是将数字列表分成几个chunck,并并行计算每个chunck的和,以加快计算速度。
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#define LENGTH 100000000
#define NTHREADS 2
#define NREPEATS 10
#define CHUNCK (LENGTH / NTHREADS)
typedef struct {
size_t id;
long *array;
long result;
} worker_args;
void *worker(void *args) {
worker_args *wargs = (worker_args*) args;
const size_t start = wargs->id * CHUNCK;
const size_t end = wargs->id == NTHREADS - 1 ? LENGTH : (wargs->id+1) * CHUNCK;
for (size_t i = start; i < end; ++i) {
wargs->result += wargs->array[i];
}
return NULL;
}
int main(void) {
long* numbers = malloc(sizeof(long) * LENGTH);
for (size_t i = 0; i < LENGTH; ++i) {
numbers[i] = i + 1;
}
worker_args *args = malloc(sizeof(worker_args) * NTHREADS);
for (size_t i = 0; i < NTHREADS; ++i) {
args[i] = (worker_args) {
.id = i,
.array = numbers,
.result = 0
};
}
pthread_t thread_ids[NTHREADS];
for (size_t i = 0; i < NTHREADS; ++i) {
pthread_create(thread_ids+i, NULL, worker, args+i);
}
for (size_t i = 0; i < NTHREADS; ++i) {
pthread_join(thread_ids[i], NULL);
}
long sum = 0;
for (size_t i = 0; i < NTHREADS; ++i) {
sum += args[i].result;
}
printf("Run %2zu: total sum is %ld\n", n, sum);
free(args);
free(numbers);
}
Why use more threads make the program run slower?
创建和加入线程会产生开销。如果线程无事可做,那么这种开销可能比实际工作更昂贵。
你的线程只是在做一个简单的求和,并不那么昂贵。还要考虑从例如10 到 11 个线程不会改变每个线程的工作负载。
10 个线程 --> 每个线程 10000000 个总和
11 个线程 --> 每个线程 9090909 个总和
创建额外线程的开销可能超过每个线程 "work load saved"。
在我的 PC 上,程序运行时间不到 100 毫秒。多线程不值得麻烦。
在多线程值得做之前,您需要处理更密集的任务。
另请注意,创建多于计算机内核数(包括超线程)的线程很少有意义。
false sharing
是的,"false sharing" 会影响多线程程序的性能,但我怀疑这是您遇到的真正问题。
"false sharing" 是在(某些)缓存系统中发生的事情,当两个线程(或者更确切地说是两个内核)写入属于同一缓存行的两个不同变量时。在这种情况下,两个 threads/cores 竞争拥有缓存行(用于写入),因此,他们将不得不一次又一次地刷新内存和缓存。这对性能不利。
正如我所说 - 我怀疑这是你的问题。一个聪明的编译器会让你的循环只使用 CPU 寄存器并且只在最后写入内存。您可以检查代码的反汇编,看看是否属于这种情况。
您可以通过增加结构的 sizeof 来避免 "false sharing",这样每个结构都适合您系统上缓存行的大小。