使用OpenMP的C++并行执行比串行慢,计时方式是否正确?
关于OpenMP并行执行比串行慢的问题分析
我完全懂你现在的困惑——明明给代码加了OpenMP并行指令,结果跑起来反而比纯串行还慢,换谁都会先怀疑是不是计时出了问题。咱们一步步拆解这个问题,找到根源和解决办法:
可能的核心原因
1. 计时函数选得不对
你当前用的clock()函数,统计的是CPU总时钟周期,不是实际的墙钟时间(也就是我们感知到的“真实运行时间”)。在并行程序里,多个线程的CPU时间会被累加,哪怕实际运行更快,clock()返回的数值也可能比串行程序更大,这就造成了“并行更慢”的错觉。
2. 并行开销盖过了计算收益
你的循环迭代次数num_steps = 100000其实不算大,而OpenMP创建线程、线程调度、甚至隐含的同步操作都是有成本的。当计算任务的粒度太小时,这些并行带来的额外开销会超过多线程的加速效果,最终导致整体耗时反增。
3. 数据竞争拖慢了并行效率
你代码里的sum是共享变量,多个线程同时对它写入会引发数据竞争。OpenMP在这种场景下可能会隐式加入同步锁,这会让并行线程频繁等待,效率直接跌到串行以下。
针对性解决建议
第一步:换用正确的计时方式
改用OpenMP自带的omp_get_wtime(),它专门用来测量墙钟时间,能准确反映程序实际运行时长:
#include "stdafx.h" #include <omp.h> #include <iostream> using namespace std; static long num_steps = 100000; double step; double pi; int main() { double start_time = omp_get_wtime(); // 替换clock() int i; double x, sum = 0.0; step = 1.0 / (double)num_steps; // 用reduction消除数据竞争 #pragma omp parallel for reduction(+:sum) private(x) for (i = 0; i < num_steps; i++) { x = (i + 0.5) * step; sum += 4.0 / (1.0 + x*x); } pi = sum * step; double end_time = omp_get_wtime(); cout << "计算得到的Pi值:" << pi << endl; cout << "实际运行耗时:" << end_time - start_time << " 秒" << endl; return 0; }
第二步:优化并行粒度
- 增大迭代次数:比如把
num_steps改成1e7甚至更大,让每个线程有足够多的计算量,抵消并行的启动开销。 - 手动指定线程数:根据你的CPU核心数,用
#pragma omp parallel num_threads(4)(比如4核就设4)避免OpenMP创建过多线程导致调度混乱。
第三步:消除数据竞争
用reduction(+:sum)子句代替shared(sum),让每个线程先计算自己的局部和,最后再合并成全局和——彻底避免多个线程同时写入共享变量的竞争问题,这是提升并行效率的关键。
验证步骤
- 用修正后的代码重新测试,对比串行和并行的墙钟时间(也就是
omp_get_wtime()的差值)。 - 逐步增大
num_steps的数值,你会发现当计算量足够大时,并行的性能优势会明显体现出来。
内容的提问来源于stack exchange,提问作者Raghav venkat
相关产品推荐
相关产品推荐

