尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

PMPP并行直方图全解:Atomic原子操作vs私有化计数,消灭性能瓶颈

PMPP并行直方图全解:Atomic原子操作vs私有化计数,消灭性能瓶颈 PMPP并行直方图全解Atomic原子操作vs私有化计数消灭性能瓶颈【免费下载链接】pmppComplete solutions to the Programming Massively Parallel Processors Edition 4项目地址: https://gitcode.com/gh_mirrors/pm/pmppPMPP《Programming Massively Parallel Processors》第4版完整解答集中的并行直方图案例是GPU新手理解原子操作与私有化计数两种加速策略的最佳入门教程。本文带你逐层拆解从最朴素的 atomicAdd 到共享内存私有化的完整优化路径彻底消灭直方图计算的性能瓶颈。为什么并行直方图是GPU性能瓶颈的典型想象一个场景你要统计一百万个随机字母a~z的出现次数把字母按每4个一组归入 7 个桶bin。CPU 上直接histo[bin]就完事了但在 GPU 上成千上万个线程同时执行同一个读-改-写操作就会互相踩脚两个线程同时给同一个桶 1结果可能丢一个唯一正确的做法是使用原子操作atomicAdd让硬件保证操作的原子性但原子操作必须串行化执行——所有线程都在抢同一个内存地址这就是著名的内存争用瓶颈。本章代码 histogram.cu 实现了5 个版本的直方图并做了完整基准测试是学习并行直方图优化技巧的一手材料。版本一最朴素的原子操作直方图最直接的写法每个线程处理一个字符直接对全局内存做原子加// [histogram.cu](https://link.gitcode.com/i/6eaed569f8009bc47896becaa3e3c667#L74-L82) __global__ void histo_kernel(char* data, unsigned int length, unsigned int* histo) { unsigned int i blockIdx.x * blockDim.x threadIdx.x; if (i length) { int alphabet_position data[i] - a; if (alphabet_position 0 alphabet_position 26) { atomicAdd(histo[alphabet_position / BIN_SIZE], 1); } } }问题输入有多大全局内存原子操作就有多少次。以书中 524,288 个元素的例子计算就是524,288 次全局原子操作全部排队串行执行——瓶颈一目了然。 书中习题估算DRAM 上同址原子操作延迟约 100ns理论吞吐仅1000万次/秒若 90% 命中 L2 缓存则提升到7300万次/秒。相比 GPU 的算力这个速度低了好几个数量级。版本二全局内存私有化计数Privatization核心思想别让所有线程抢同一份直方图每个线程块block私有一份副本块内随便加此时原子操作发生在块专属地址上几乎没有争用最后再把各块的私有结果汇总回去。// [histogram.cu](https://link.gitcode.com/i/6eaed569f8009bc47896becaa3e3c667#L84-L102) atomicAdd(histo[blockIdx.x * NUM_BINS alphabet_position / BIN_SIZE], 1);汇总阶段的原子操作次数 块数 × 桶数。同样 524,288 元素、1024 线程/块、128 个桶需要 512 个块 → 只有512 × 128 65,536 次全局原子操作直接少了一个数量级。版本三共享内存私有化最快的常规版本把私有副本再搬到共享内存shared memory里块内原子操作的延迟只有 1ns 级别比全局内存快得多// [histogram.cu](https://link.gitcode.com/i/6eaed569f8009bc47896becaa3e3c667#L104-L129) __shared__ unsigned int histo_s[NUM_BINS]; // 1. 块内清零私有直方图 // 2. 每个线程对共享内存做 atomicAdd低延迟 // 3. __syncthreads() 后统一提交到全局内存流程非常清晰私有计数 → 同步 → 一次性提交。提交阶段的争用被压缩到最少。书中习题给出估算同样的工作量私有化后浮点吞吐上限从 50 MFLOPS 提升到4.55 GFLOPS提升近百倍。版本四线程粗化Thread Coarsening——再砍一次开销私有化后每个块都有清零 提交的固定开销块越多开销越大。于是引入线程粗化让每个线程一次处理多个元素代码中CFACTOR 32从而发射更少的块// [histogram.cu](https://link.gitcode.com/i/6eaed569f8009bc47896becaa3e3c667#L131-L156) for (unsigned int i tid * CFACTOR; i min((tid 1) * CFACTOR, length); i) { ... atomicAdd(histo_s[alphabet_position / BIN_SIZE], 1); }书中计算粗化因子为 4 时块数从 512 降到 128提交阶段原子操作进一步降到128 × 128 16,384 次。最终版本还配合了合并内存访问coalesced access确保线程读取数据时连续高效。5 个版本横向对比原子操作次数一览以书中 524,288 元素、128 桶、1024 线程/块为例计算过程详见 chapter-09/README.md 的 Exercise 6版本全局原子操作次数减少比例朴素 atomicAdd524,288—全局内存私有化65,536↓ 87.5%共享内存私有化65,536↓ 87.5%但单次更快共享内存 线程粗化×416,384↓ 96.9%规律私有化把每元素一次原子操作变成每块每桶一次粗化再进一步减少块数。这就是并行直方图优化的两大核心武器。如何跑基准测试3步上手 环境需要 NVIDIA GPU 与 CUDA 工具包仓库克隆地址git clone https://gitcode.com/gh_mirrors/pm/pmpp cd pmpp/chapter-09/code nvcc histogram.cu -o histogram ./histogram程序会处理 1 亿个随机字符依次基准测试 5 种并行实现和 1 个串行版本每轮前还会清空 L2 缓存保证公平见 benchmark_histogram最后打印各版本的平均耗时与加速比并自动校验 6 组直方图结果是否完全一致——你可以直接看到消灭瓶颈后的真实差距。总结并行直方图优化三板斧私有化Privatization每块一份私有直方图把全局争用降为块内局部争用共享内存把私有副本放入 shared memory原子操作延迟降低一个数量级线程粗化单线程干更多活减少块的固定开销提交阶段原子操作再降 4 倍。这套私有化 共享内存 粗化的组合拳是 GPU 并行计算中处理写同一地址类问题计数、累加、投票的通用范式。完整实现与习题解答都在 chapter-09/配合 histogram.cu 源码逐段对照一个下午就能吃透。【免费下载链接】pmppComplete solutions to the Programming Massively Parallel Processors Edition 4项目地址: https://gitcode.com/gh_mirrors/pm/pmpp创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表