C++多线程图像填充性能差异咨询:按行拆分为何更快?
为什么按行拆分的多线程图像填充性能远超按列拆分?
我在研究C++多线程编程时做了一组性能测试:目标是将2560×1440的图像数组(元素为std::array<int,3>)全部填充为白色{255,255,255},分别测试了单线程、两种16线程(机器硬件支持16线程)的实现,结果差异显著。
测试代码
单线程实现
vector<array<int,3>> image { 2560*1440 }; // 计时开始... for (std::size_t i=0; i<1440; ++i) for (std::size_t j=0; j<2560; ++j) image[i*2560+j] = {255,255,255};
第一种多线程(按列拆分)
vector<array<int,3>> image { 2560*1440 }; auto n = thread::hardware_concurrency(); auto execution = [&](std::size_t id) { for (std::size_t i=0; i<1440; ++i) for (std::size_t j=id; j<2560; j+=n) image[i*2560+j] = {255,255,255}; };
第二种多线程(按行拆分)
vector<array<int,3>> image { 2560*1440 }; auto n = thread::hardware_concurrency(); auto execution = [&](std::size_t id) { for (std::size_t i=id; i<1440; i+=n) for (std::size_t j=0; j<2560; ++j) image[i*2560+j] = {255,255,255}; };
性能测试结果
- 单线程:28076微秒
- 按列拆分多线程:24688微秒
- 按行拆分多线程:4073微秒
原因分析
核心原因不是指针移动次数,而是CPU缓存的空间局部性原理带来的缓存命中率差异,这是影响内存密集型程序性能的关键因素:
内存布局特性:
std::vector是连续内存容器,你的图像采用行优先存储(i为行索引,j为列索引),所以同一行的所有像素在内存中是连续排列的;而同一列的像素,内存地址间隔为2560 * sizeof(std::array<int,3>)(即2560*12=30720字节),远大于CPU缓存行的典型大小(64字节)。按行拆分的缓存效率:每个线程负责连续的几行像素,访问的内存地址是连续的。CPU的缓存预取器会自动将后续连续的内存块加载到L1/L2缓存中,几乎每个内存访问都能命中缓存——缓存的访问速度比主存快10~100倍,这直接带来了性能的爆炸式提升。
按列拆分的缓存低效:每个线程访问的是每一行的第
id列,内存地址是跳跃式的,每次访问的地址间隔极大,缓存根本无法容纳这些分散的数据。绝大多数内存访问都需要从主存加载,主存的高延迟直接拖慢了整体速度,甚至仅比单线程略快——因为单线程按行遍历至少还能保证不错的缓存命中率。
指针移动的开销在这个场景里完全可以忽略,真正的性能瓶颈是内存访问的缓存效率。
内容的提问来源于stack exchange,提问作者Code4Fun
相关产品推荐
相关产品推荐

