多线程求和程序运行慢于单线程的问题排查与优化求助
多线程求和比单线程慢?原因和修复方法
你的多线程求和程序跑不过单线程,核心是线程开销盖过了并行收益,再加上一些额外的冗余操作,具体原因和修复方案如下:
问题根源
- 线程创建/销毁的固定开销太高:
pthread_create和pthread_join需要操作系统做线程调度、资源分配,这部分有固定成本。如果你的n不算特别大,每个线程只做一点点计算,线程管理的时间会比实际求和的时间还长,自然跑不过单线程。 - 动态内存分配纯纯浪费:每个线程用
calloc分配内存存结果,最后还没释放内存(代码里漏了free(sum),会导致内存泄漏),这不仅增加了内存分配的开销,还引入了不必要的复杂度。 - 单线程被编译器开挂优化:单线程的循环求和逻辑简单,编译器直接会把它优化成数学公式
n*(n+1)/2,相当于常数时间运行;而多线程代码因为涉及线程函数、跨线程数据传递,编译器很难做这种全局优化。
修复方案
针对这些问题,我们可以从三个方向优化:
去掉动态内存分配,直接传结果指针
不在线程里分配内存,而是在主线程里提前为每个线程准备好结果变量,通过结构体把指针传给线程函数,省去内存分配的开销,也避免泄漏。小n直接用单线程
只有当n足够大(比如千万级以上),多线程的并行收益才会超过线程开销。加个判断,小n的时候直接用单线程甚至公式计算,效率更高。开编译器优化
编译时加上-O2或-O3选项,让编译器对多线程代码也做足够优化,比如循环展开、寄存器优化等。
修改后的代码
#include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <sys/time.h> typedef struct data{ unsigned long long start; unsigned long long end; unsigned long long* result; // 直接传递结果存储地址 } data; void* threadSum( void* p) { data* ptr = (data*)p; *ptr->result = 0; for (unsigned long long i = ptr->start; i <= ptr->end; i++) { *ptr->result += i; } pthread_exit(NULL); } int main(int argc, char* argv[]) { if (argc != 3) { fprintf(stderr, "用法: %s <线程数> <n>\n", argv[0]); return 1; } unsigned long long n = atoll(argv[2]); int numthread = atoi(argv[1]); struct timeval start, end; // 小n直接用单线程公式计算,避免线程开销 if (n < 1000000 || numthread <= 1) { gettimeofday(&start, NULL); unsigned long long result = n * (n + 1) / 2; gettimeofday(&end, NULL); printf("结果 = %llu\n", result); double time_taken = (end.tv_sec - start.tv_sec) + (end.tv_usec - start.tv_usec)/1e6; printf("耗时: %f\n", time_taken); return 0; } gettimeofday(&start, NULL); // 动态分配内存,避免栈溢出(线程数大时栈上数组可能不够) data* thread_data = malloc(numthread * sizeof(data)); pthread_t* tid = malloc(numthread * sizeof(pthread_t)); unsigned long long* thread_results = calloc(numthread, sizeof(unsigned long long)); // 划分任务区间 for(int i = 0; i < numthread; i++) { thread_data[i].start = i * n / numthread + 1; thread_data[i].end = (i + 1) * n / numthread; thread_data[i].result = &thread_results[i]; pthread_create(&tid[i], NULL, threadSum, &thread_data[i]); } unsigned long long result = 0; for(int i = 0; i < numthread; i++) { pthread_join(tid[i], NULL); result += thread_results[i]; } printf("结果 = %llu\n", result); gettimeofday(&end, NULL); double time_taken = (end.tv_sec - start.tv_sec) + (end.tv_usec - start.tv_usec)/1e6; printf("耗时: %f\n", time_taken); // 清理内存 free(thread_data); free(tid); free(thread_results); return 0; }
注意事项
- 编译命令:
gcc -O2 sum_thread.c -o sum_thread -lpthread,一定要加-O2优化和-lpthread链接线程库。 - 当
n达到1e8以上时,多线程版本的速度会明显超过单线程循环(如果单线程用公式还是最快,但这里对比的是循环实现)。 - 代码里加了参数检查,避免非法输入导致程序崩溃。
内容的提问来源于stack exchange,提问作者Caroliana
相关产品推荐
相关产品推荐

