C Pthread矩阵乘法多线程比单线程慢12倍问题求助
兄弟,你这情况简直是新手入门多线程的经典踩坑现场!咱们先直接点出核心问题,再一步步给你拆解和优化方向:
核心问题:线程粒度太小,开销远大于计算收益
你竟然给每个矩阵元素的计算都单独创建一个线程!比如当n=100时,就要创建10000个线程——线程创建和销毁的开销(分配栈空间、内核数据结构初始化、调度切换)是非常大的,而单个元素的计算只是n次乘加,这点计算量完全抵不上线程开销的零头,这就是多线程比单线程慢12倍的根本原因。
次要问题:计时逻辑有偏差
看你的代码:
- 单线程模式下,你把每个元素的计算时间累加起来;
- 多线程模式下,只统计了
pthread_join的时间,完全没算创建所有线程的开销!
就算这样都慢了12倍,可见线程创建的开销有多恐怖。正确的做法应该是对整个矩阵乘法过程计时,从函数开始到结束,这样才能公平对比单线程和多线程的真实耗时。
额外影响:缓存一致性问题(次要)
每个线程独立写入out[i][ii],如果多个线程的写入操作落在同一个CPU缓存行上,会引发缓存颠簸(缓存失效、同步),进一步降低性能,但这个在你的场景里不是主要问题,核心还是线程粒度的锅。
优化方案,立刻见效!
1. 调整线程粒度:按行/按块分配任务
不要给每个元素开线程,而是让一个线程负责一整行或者一个子块的计算。线程数量最好和你的CPU核心数匹配(比如4核就开4个线程),这样线程开销可以忽略,计算任务足够大,能真正发挥多线程的并行优势。
比如修改后的线程函数负责一行的计算:
// 定义新的参数结构体 struct rowArgs { int n; int row; int** first; int** second; int** out; }; void *vectorMultiplyRow(void *arguments){ struct rowArgs *args = arguments; int n = args->n; int row = args->row; int **first = args->first; int **second = args->second; int **out = args->out; // 计算当前行的所有元素 for (int col = 0; col < n; col++) { int sum = 0; for (int k = 0; k < n; k++) { sum += first[row][k] * second[k][col]; } out[row][col] = sum; } return NULL; }
然后在multiplyMatrix里创建对应数量的线程,分配行任务:
void multiplyMatrix(int n, int** first, int** second, int** out){ int thread_count = 4; // 替换成你的CPU核心数 pthread_t tid[thread_count]; struct rowArgs args[thread_count]; clock_t start = clock(); if(!SINGLETHREAD){ // 给每个线程分配连续的行 int rows_per_thread = n / thread_count; for (int i = 0; i < thread_count; i++) { args[i].n = n; args[i].row = i * rows_per_thread; args[i].first = first; args[i].second = second; args[i].out = out; pthread_create(&tid[i], NULL, vectorMultiplyRow, (void *)&args[i]); } // 处理剩余的行(如果n不能整除thread_count) for (int row = thread_count * rows_per_thread; row < n; row++) { for (int col = 0; col < n; col++) { int sum = 0; for (int k = 0; k < n; k++) { sum += first[row][k] * second[k][col]; } out[row][col] = sum; } } // 等待所有线程完成 for(int i=0; i < thread_count; i++){ pthread_join(tid[i], NULL); } } else { // 单线程逻辑 for (int i = 0; i < n; i++) { for (int col = 0; col < n; col++) { int sum = 0; for (int k = 0; k < n; k++) { sum += first[i][k] * second[k][col]; } out[i][col] = sum; } } } clock_t end = clock(); totalTime = (double)(end - start) / CLOCKS_PER_SEC; }
2. 统一计时逻辑
上面的代码已经修正了计时:对整个乘法过程从start到end计时,这样单线程和多线程的耗时对比才公平。
3. 进阶:使用线程池(可选)
如果需要频繁执行矩阵乘法,可以提前创建一组线程(线程池),每次把任务提交给线程池,避免重复创建销毁线程的开销。不过对于新手来说,先调整线程粒度就能看到明显的提速效果。
总结
你现在的问题完全是“用大炮打蚊子”——用开销极大的细粒度线程去做极小的计算任务,完全本末倒置了。调整线程粒度到和CPU核心匹配的程度,你立刻就能看到多线程的提速效果。
内容的提问来源于stack exchange,提问作者hamish sams

