You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++中vector分片多线程处理的性能问题咨询

C++ Vector分片多线程处理问题

我在对vector进行分片多线程处理时遇到了一些问题,相关代码如下:

size_t sz = all_objects.size();
size_t np = config.n_threads;
size_t part = sz / np;
vector<vector<int>> solutions(np);

说明:无法预先知晓solutions中每个元素的大小。

随后为线程准备任务:

auto paraTask = [&](size_t start, size_t end, vector<int> &sol) {
    for (size_t l = start; l < end; ++l)
        fun({all_objects[l]}, sol);
};
for (size_t i = 0; i < np; i++) {
    size_t start = i * part;
    size_t length = (i + 1 == np) ? sz - i * part : part;
    threads[i] = std::thread(paraTask, start, start + length, std::ref(solutions[i]));
}

其中函数fun会写入sol。之后调用join等待线程结束:

for (auto &&thread: threads) thread.join();

我的疑问:

  • 对代码各部分计时后发现,join阶段耗时最长,我认为这是因为join需等待所有线程结束,该理解是否正确?
  • 测试可扩展性时,1线程无并行,到4线程时性能提升,但8、16线程时计算时间反而增加,为何会出现这种情况?
  • 不同线程操作同一vector的不同子vector(且未预先为子vector分配内存),是否会因内存位置邻近导致线程互相等待?若如此,为何仅在4线程后出现该问题?

补充说明:

  1. 从代码设计看,线程不会写入同一变量,但不确定实际中操作同一vector的不同子vector是否会出现冲突。
  2. 硬件环境:CPU为11th Gen Intel(R) Core(TM) i7-11700KF @ 3.60GHz,16GB DDR4内存,Windows 11系统,编译器为MS_VS 2022。

问题解答

1. 关于join阶段耗时最长的理解

你的理解完全正确。std::thread::join()是阻塞调用,必须等目标线程执行完毕才会返回。循环中逐个调用join时,整个阶段的总耗时等于耗时最长的那个线程的执行时间,因为前面的join会依次等待对应线程完成,最后一个join要等最晚结束的线程,所以计时结果会显示join阶段耗时最长。

2. 8/16线程性能下降的原因

你的CPU是8物理核心、16逻辑核心(超线程),性能变化的核心原因和硬件特性、任务类型有关:

  • 4线程时,每个线程独占一个物理核心,无核心资源竞争,并行效率最高,所以性能提升明显。
  • 8线程时刚好占满所有物理核心,如果是纯计算密集型任务,性能应接近线性提升,但如果任务包含较多内存操作,此时可能触发内存带宽瓶颈,提升幅度会打折扣。
  • 16线程时启用超线程,每个物理核心同时跑两个逻辑线程。超线程仅在单核心有空闲流水线时能提升利用率,若任务是计算密集型,两个逻辑线程会竞争同一物理核心的执行资源(如ALU、缓存),加上上下文切换开销增加,会导致每个线程的执行效率下降,整体总耗时上升。

另外还要注意负载均衡问题:你的分片方式是前np-1个线程处理part个元素,最后一个线程处理剩余元素。如果sz无法被np整除,最后一个线程的任务量可能和其他线程差异较大,线程数越多,这种不均衡导致的资源浪费越明显,进一步拉低性能。

3. 子vector内存邻近是否导致线程等待

这种情况确实可能发生,但不是直接的线程等待,而是缓存行伪共享(False Sharing):
vector<vector<int>> solutions(np)中,每个子vector的元数据(指针、大小、容量)存储在solutions的连续内存块中。当多个线程同时修改不同子vector时,如果这些元数据刚好落在同一个CPU缓存行里,一个线程修改自己的子vector元数据(比如push_back触发扩容时更新容量字段)会让整个缓存行失效,其他线程访问自身子vector元数据时需要重新从内存加载,增加延迟。

4线程时未出现该问题,是因为此时线程数少于物理核心数,线程可能分布在不同核心组,缓存行冲突的概率较低;当线程数增加到8或16时,更多线程同时操作邻近的元数据,缓存行失效频率大幅上升,性能下降的现象就显现出来了。

你可以尝试两种优化方式:如果能估算子vector的大小,提前为每个子vector分配足够内存,减少元数据的修改;或者把solutions改成vector<unique_ptr<vector<int>>>,让每个子vector的元数据分散在不同内存位置,从根源避免伪共享。


内容的提问来源于stack exchange,提问作者Claudio Tomasi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 16:55:40