为什么这个关于共享变量的简单程序不能扩展? (无锁)
Why this simple program on shared variable does not scale? (no lock)
我是并发编程的新手。我实施了一项 CPU 密集型工作并测量了我可以获得多少加速。但是,随着#threads 的增加,我无法获得任何加速。
该程序执行以下任务:
- 有一个从 1 到 1000001 计数的共享计数器。
- 每个线程执行以下操作,直到计数器达到 1000001:
- 自动递增计数器,然后
- 运行循环10000次
总共有 1000001*10000 = 10^10 个操作要执行,所以我应该能够在增加#threads 时获得良好的加速。
以下是我的实现方式:
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdatomic.h>
pthread_t workers[8];
atomic_int counter; // a shared counter
void *runner(void *param);
int main(int argc, char *argv[]) {
if(argc != 2) {
printf("Usage: ./thread thread_num\n");
return 1;
}
int NUM_THREADS = atoi(argv[1]);
pthread_attr_t attr;
counter = 1; // initialize shared counter
pthread_attr_init(&attr);
const clock_t begin_time = clock(); // begin timer
for(int i=0;i<NUM_THREADS;i++)
pthread_create(&workers[i], &attr, runner, NULL);
for(int i=0;i<NUM_THREADS;i++)
pthread_join(workers[i], NULL);
const clock_t end_time = clock(); // end timer
printf("Thread number = %d, execution time = %lf s\n", NUM_THREADS, (double)(end_time - begin_time)/CLOCKS_PER_SEC);
return 0;
}
void *runner(void *param) {
int temp = 0;
while(temp < 1000001) {
temp = atomic_fetch_add_explicit(&counter, 1, memory_order_relaxed);
for(int i=1;i<10000;i++)
temp%i; // do some CPU intensive work
}
pthread_exit(0);
}
但是,在我 运行 我的程序中,我无法获得比顺序执行更好的性能!!
gcc-4.9 -std=c11 -pthread -o my_program my_program.c
for i in 1 2 3 4 5 6 7 8; do \
./my_program $i; \
done
Thread number = 1, execution time = 19.235998 s
Thread number = 2, execution time = 20.575237 s
Thread number = 3, execution time = 25.161116 s
Thread number = 4, execution time = 28.278671 s
Thread number = 5, execution time = 28.185605 s
Thread number = 6, execution time = 28.050380 s
Thread number = 7, execution time = 28.286925 s
Thread number = 8, execution time = 28.227132 s
我运行4核机器上的程序。
有没有人有改进程序的建议?或者我为什么无法获得加速的任何线索?
这里唯一可以并行完成的工作是循环:
for(int i=0;i<10000;i++)
temp%i; // do some CPU intensive work
gcc,即使采用最低优化级别,也不会为 temp%i;
void 表达式发出任何代码(反汇编并查看),因此这实际上变成了一个空循环,执行速度非常快 -在不同内核上有多个线程 运行 的情况下,执行时间将由包含不同内核之间的原子变量乒乓的缓存行决定。
您需要让此循环实际完成大量工作,然后才能看到加速。
我是并发编程的新手。我实施了一项 CPU 密集型工作并测量了我可以获得多少加速。但是,随着#threads 的增加,我无法获得任何加速。
该程序执行以下任务:
- 有一个从 1 到 1000001 计数的共享计数器。
- 每个线程执行以下操作,直到计数器达到 1000001:
- 自动递增计数器,然后
- 运行循环10000次
总共有 1000001*10000 = 10^10 个操作要执行,所以我应该能够在增加#threads 时获得良好的加速。
以下是我的实现方式:
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdatomic.h>
pthread_t workers[8];
atomic_int counter; // a shared counter
void *runner(void *param);
int main(int argc, char *argv[]) {
if(argc != 2) {
printf("Usage: ./thread thread_num\n");
return 1;
}
int NUM_THREADS = atoi(argv[1]);
pthread_attr_t attr;
counter = 1; // initialize shared counter
pthread_attr_init(&attr);
const clock_t begin_time = clock(); // begin timer
for(int i=0;i<NUM_THREADS;i++)
pthread_create(&workers[i], &attr, runner, NULL);
for(int i=0;i<NUM_THREADS;i++)
pthread_join(workers[i], NULL);
const clock_t end_time = clock(); // end timer
printf("Thread number = %d, execution time = %lf s\n", NUM_THREADS, (double)(end_time - begin_time)/CLOCKS_PER_SEC);
return 0;
}
void *runner(void *param) {
int temp = 0;
while(temp < 1000001) {
temp = atomic_fetch_add_explicit(&counter, 1, memory_order_relaxed);
for(int i=1;i<10000;i++)
temp%i; // do some CPU intensive work
}
pthread_exit(0);
}
但是,在我 运行 我的程序中,我无法获得比顺序执行更好的性能!!
gcc-4.9 -std=c11 -pthread -o my_program my_program.c
for i in 1 2 3 4 5 6 7 8; do \
./my_program $i; \
done
Thread number = 1, execution time = 19.235998 s
Thread number = 2, execution time = 20.575237 s
Thread number = 3, execution time = 25.161116 s
Thread number = 4, execution time = 28.278671 s
Thread number = 5, execution time = 28.185605 s
Thread number = 6, execution time = 28.050380 s
Thread number = 7, execution time = 28.286925 s
Thread number = 8, execution time = 28.227132 s
我运行4核机器上的程序。
有没有人有改进程序的建议?或者我为什么无法获得加速的任何线索?
这里唯一可以并行完成的工作是循环:
for(int i=0;i<10000;i++)
temp%i; // do some CPU intensive work
gcc,即使采用最低优化级别,也不会为 temp%i;
void 表达式发出任何代码(反汇编并查看),因此这实际上变成了一个空循环,执行速度非常快 -在不同内核上有多个线程 运行 的情况下,执行时间将由包含不同内核之间的原子变量乒乓的缓存行决定。
您需要让此循环实际完成大量工作,然后才能看到加速。