如何优化大量.npy文件加载至神经网络训练数据生成器的速度
基础代码加载速度优化
- 循环append和列表推导式的加载性能没有本质差异,从你的基准测试结果能看到两者的单批次平均耗时几乎完全一致,不需要在循环写法上做额外优化。
- 单批次加载最直观的提速来自
mmap_mode='r'参数:你的测试显示这个方案的单批次加载耗时比原始方法降低了近60%,核心原理是该模式不会在np.load调用时立刻把整个文件内容读入物理内存,仅建立文件内容与虚拟内存地址的映射关系,因此函数本身的返回速度极快。 - 不要强行对可变T维度做全局padding拼批:每个样本的T长度不固定,为了凑成固定形状的批张量做无意义的填充,反而会引入额外内存拷贝和无效计算,甚至会完全抵消mmap带来的加载加速。
原有多进程方案性能差的核心原因
- 原有实现没有考虑进程间的序列化传输开销:每个worker进程加载完2MB的numpy数组后,需要把完整的数组内容通过进程间通信通道序列化传回主进程,单批次32个文件累计要传输64MB的内容,这个开销远高于单进程直接读文件的耗时,自然性能比基础方案更差。
- 多进程并发并不天然适合小文件读取场景:如果使用机械硬盘,多进程并发读随机位置的小文件会让磁头频繁寻道,进一步拉低磁盘IO效率;如果使用固态硬盘,进程间通信的序列化开销也很容易盖过并发读取的微小收益。
正确的多进程加载实现思路
- 减少进程间传输的数据量:不要让worker只完成文件读取就回传完整numpy数组,要把数组裁剪、类型转换、批拼接等预处理逻辑全部放到worker进程内完成,只把最终可以直接喂给神经网络的批结果传回主进程,大幅降低IPC传输的总数据量。
- 采用预取重叠机制:不要等当前批次训练完成再启动下一批次的加载,在后台维护一个固定长度的预取队列,让IO加载和GPU训练过程并行,把原本被等待的加载耗时隐藏在训练时间之下,这比单纯追求单批次加载速度的实际收益高得多。
- 严格控制并发度:根据存储类型调整并发进程数量,机械硬盘的并发数不要超过2,固态硬盘的并发数也不要超过4,过高的并发只会触发磁盘IO瓶颈,不会进一步提升读取速度。
为什么mmap模式单批加载更快,整体训练时间反而增加
你基准测试里测到的mmap快速返回,只是函数本身的耗时,不是完整加载数据的总耗时——mmap会把实际的磁盘读取操作推迟到后续真正访问数组元素时才触发。你的基准测试在加载完数组后就直接丢弃了对象,根本没有访问数组内的元素,因此测到的耗时仅为建立内存映射的开销,没有覆盖真实读盘的时间。
- 实际训练过程中,神经网络前向传播需要访问矩阵的全部元素,此时mmap会触发频繁的缺页中断,在用户态和内核态反复切换,访问效率反而低于一次性把文件读入内存的传统模式。
- 长期留存的mmap映射会占用大量页缓存空间:如果同时映射了数千个2MB的文件,这些映射的内存不会被操作系统主动回收,会挤掉原本可以用来预取训练数据的缓存空间,拉低整体的磁盘读取效率。
- 若要保留mmap的读盘效率又避免上述问题,可以在
np.load返回mmap数组后,立刻通过np.array(arr)把内容拷贝到普通内存数组中,这样既可以利用mmap的并行读盘优势,又能避免后续访问时的缺页开销。
内容的提问来源于stack exchange,提问作者Phys
相关产品推荐
相关产品推荐

