异构计算的性能账常常不是算在计算单元上,而是算在数据搬运上。主机和加速器之间通过总线或互连交换数据,每一次搬运都要付出传输时间、同步开销和内存占用三笔成本。如果数据搬运的时间超过了加速器节省的计算时间,异构加速就变成了负优化。很多异构程序在小规模测试时很快,一上真实数据量就变慢,根因往往不是核函数写得不好,而是搬运账没有算清楚。

算这笔账要先区分三种搬运场景。第一种是输入输出一次性搬运,数据从主机内存拷到加速器,算完再拷回来,成本相对固定,适合计算密度高的数据。第二种是迭代算法中的反复搬运,每轮迭代都要交换中间结果,搬运次数随迭代次数线性增长,这时哪怕单次搬运不大,累积起来也很可观。第三种是随机访问或细粒度访问,加速器需要频繁回主机取零散数据,传输效率最低,也最容易成为瓶颈。不同的场景对应不同的优化策略,不能用同一套方法处理。

衡量搬运成本有几个关键量。传输带宽决定了大数据块的搬运时间,延迟决定了小数据块的往返开销,同步粒度决定了主机和加速器之间等待的频率。工程上常用的估算方法是把一次搬运拆成固定开销加可变开销:固定开销包括启动传输、同步和地址转换,与数据量无关;可变开销与数据量成正比,由带宽决定。小数据频繁搬运时固定开销占主导,优化方向是合并传输、减少同步次数;大数据搬运时可变开销占主导,优化方向是压缩数据、减少搬运量或改用更高带宽的互连。

减少搬运的思路可以归纳为三条。第一是让数据尽量待在加速器上,中间结果不要每轮都拷回主机,只在真正需要时才同步。第二是重叠传输与计算,用双缓冲或流水线的方式让下一批数据在当前计算进行时提前传输,把等待时间藏起来。第三是改变数据布局,让加速器访问的数据在内存中连续存放,提高传输效率,避免因为碎片化访问而浪费带宽。这三条思路都不依赖特定的加速器型号,是异构编程里通用的工程常识。

排查搬运瓶颈时,建议先做一次时间分解:用性能分析工具把程序总时间拆成计算时间、传输时间和等待时间,看哪一项占比最高。如果传输时间占主导,再进一步区分是带宽不足还是同步太频繁;如果等待时间占主导,检查主机和加速器的任务划分是否均衡,是否存在一方长期空闲等另一方的情况。把这笔搬运账算清楚、写进设计文档,后续换硬件、换数据规模时才能快速判断加速比是否还成立,而不是每次都凭感觉调参数。