使用TBB后C++程序性能慢2倍且parallel_for未并行执行问题咨询
循环未并行执行的原因
- grainsize设置错误:你代码中
grainsize设为1000,但待处理的列表总长度只有200,远小于grainsize值。TBB的blocked_range会将整个范围作为单个任务块分配,不会拆分到多线程执行,自然呈现串行执行的效果。 - 同步操作放大串行表现:你在循环体中调用
std::cout打印日志,std::cout的线程安全实现依赖全局锁,即使真的多线程执行,也会因为抢锁导致执行逻辑被强制串行化,同时会带来极高的额外开销。 - 代码存在数据竞争风险:你代码中
min、clus、modif等变量看起来是lambda外部的共享变量,没有做线程局部处理或者原子保护,属于未定义行为,部分场景下也会干扰TBB的正常调度。
小型for循环的TBB性能优化方案
- 合理设置任务粒度:grainsize的取值建议为「总元素数 / (24倍的CPU物理核心数)」,对于总长度200的列表,grainsize设为1020即可,保证任务可以被拆分成足够多的块分配给多线程处理。如果不想手动计算,也可以使用
tbb::auto_partitioner让TBB自动调整任务拆分粒度。 - 合并小循环降低调度开销:TBB的
parallel_for本身有任务调度的固定开销,如果单个循环的计算量很小,单独调用parallel_for的收益很容易被调度开销抵消。你可以把多个独立的小型for循环逻辑合并到同一个parallel_for中执行,或者使用tbb::parallel_invoke并行执行多个独立的小循环,减少调度次数。 - 规避共享变量同步开销:对于循环内的累加变量(比如你的
modif),不要直接在多线程中操作共享变量,建议使用tbb::combinable做线程局部累加,最后再合并结果,或者改用tbb::parallel_reduce实现累加逻辑,避免原子操作或者锁的开销。 - 移除不必要的同步操作:正式运行时要删掉循环内的
std::cout这类带锁的操作,这类操作的开销通常远大于你循环本身的计算开销,会严重抵消并行收益。 - 控制合理的并行上限:你设置
max_allowed_parallelism为1000完全没有必要,并行数超过CPU物理核心数之后,会带来大量的上下文切换开销,反而降低性能,建议这个值设置为和CPU物理核心数一致即可,或者直接不设置,使用TBB的默认配置。 - 提前初始化TBB调度器:如果你的程序规模很小,TBB线程池的初始化开销可能占比很高,可以在程序启动时提前触发一次空的
parallel_for调用,完成线程池的初始化,避免后续业务逻辑执行时的冷启动开销。
内容的提问来源于stack exchange,提问作者MikeChouinard
相关产品推荐
相关产品推荐

