C++多线程程序运行耗时比单线程更长的原因排查
计时API使用错误
你代码中用的clock()函数统计的是进程所有线程占用的CPU时间总和,不是现实中流逝的墙钟时间。当两个线程并行运行在不同CPU核心上时,两个线程的CPU耗时会被累加计算:比如两个线程各跑了4秒,clock()返回的时间差就会是8秒左右,和你测得的8.5秒结果吻合。单线程版本是顺序执行代码,CPU时间和实际流逝时间一致,所以显示为4.96秒。这个计时结果本身就不能用来对比多线程和单线程的实际运行速度,要测真实耗时应该使用std::chrono::steady_clock这类墙钟计时器。伪共享(False Sharing)带来巨量额外开销
代码中even_sum和odd_sum是连续声明的全局变量,内存地址紧挨着,几乎一定会落在同一个CPU缓存行(主流CPU的缓存行大小为64字节)。两个运行在不同核心的线程频繁修改这两个变量时,CPU的MESI缓存一致性协议会不断在两个核心之间来回转移该缓存行的所有权:核心1修改even_sum后,核心2对应的缓存行会失效,必须重新从内存/其他核心同步数据;核心2修改odd_sum后,核心1的缓存行又会失效。这种反复的缓存同步开销远大于循环、奇偶判断、计数本身的计算量,直接抵消了多线程的并行收益,甚至带来额外的性能损耗。任务拆分逻辑不合理
你当前的多线程实现中,两个线程各自完整遍历了1~1000000000的全量区间,一个只统计偶数、一个只统计奇数,总循环次数和单线程版本完全一致(都是20亿次循环),根本没有通过并行减少计算总量。正确的拆分方式应该是把待遍历的数值区间切分成多段,每个线程只处理自己负责的区间段,同时统计段内的奇数和偶数,总循环次数可以直接降到10亿次,才能真正发挥多线程的并行优势。
修正后参考代码
#include <iostream> #include <thread> #include <chrono> using namespace std; // 按缓存行大小对齐,彻底避免伪共享 struct alignas(64) RangeResult { long long even_cnt = 0; long long odd_cnt = 0; }; void countInRange(int start, int end, RangeResult& res) { for (int i = start; i < end; ++i) { if (i % 2 == 0) { res.even_cnt++; } else { res.odd_cnt++; } } } void multiThreadVersion() { RangeResult res1, res2; // 把10亿数据拆成两半分给两个线程 thread t1(countInRange, 1, 500000000, ref(res1)); thread t2(countInRange, 500000000, 1000000000, ref(res2)); t1.join(); t2.join(); long long total_even = res1.even_cnt + res2.even_cnt; long long total_odd = res1.odd_cnt + res2.odd_cnt; cout << total_even << " " << total_odd << endl; } void singleThreadVersion() { RangeResult res; countInRange(1, 1000000000, res); cout << res.even_cnt << " " << res.odd_cnt << endl; } int main() { auto start = chrono::steady_clock::now(); // 切换注释测试不同版本 multiThreadVersion(); // singleThreadVersion(); auto end = chrono::steady_clock::now(); double cost_ms = chrono::duration_cast<chrono::milliseconds>(end - start).count(); cout << "耗时: " << cost_ms / 1000.0 << "s" << endl; return 0; }
内容的提问来源于stack exchange,提问作者Dorian Bajorek

