np.memmap通过按需加载磁盘块避免内存爆炸,但须严格指定dtype、shape、mode;多进程需各自创建实例,只读模式防缓存污染,ufunc运算须用out=参数并flush,切片须边界检查,稀疏数据应优先用scipy.sparse。

用 np.memmap 替代全量加载,但必须避开 mode='r+' 多进程共享陷阱
每个 worker 进程必须独立创建自己的 np.memmap 实例,不能在主进程定义后传给子进程。否则所有进程会竞争同一文件句柄,引发写冲突或静默数据损坏。生成器函数内部应每次重新打开:np.memmap('data.bin', mode='r', dtype=np.float32, shape=(N,))。只读模式(mode='r')能避免 OS 缓存脏页,显著降低内存驻留量。
二元运算别用 a[:] - b[:],改用 np.subtract(a, b, out=result)
写法 result[:] = a[:] - b[:] 会强制 NumPy 在内存中构建完整中间数组,哪怕 a 和 b 都是 memmap。对两个 16 GiB 的 int64 数组,这一步就申请额外 16 GiB RAM,直接触发 OOM。正确路径是调用支持 out= 参数的 ufunc:np.subtract(a, b, out=result),其中 result 必须是 mode='w+' 或 'r+' 的 memmap,且 dtype/shape 严格匹配。别忘了最后调用 result.flush()——否则数据可能卡在 OS 缓冲区没落盘。
切片操作必须加边界检查,否则越界会触发整块加载
np.memmap 的“按需加载”只在合法索引范围内生效。一旦写 data[1000000000:] 这种越界切片,NumPy 会回退到全量加载整个映射区域来报错,瞬间吃光内存。实际分块时务必显式截断:
- start = batch_i * batch_size
- end = min(start + batch_size, total_len)
- batch = data[start:end]
建议把 total_len 作为参数传入,不要依赖 len(data)——后者可能触发 mmap 全量解析。
稀疏场景优先用 scipy.sparse,别硬扛稠密矩阵
共现矩阵、用户-物品评分矩阵这类天然稀疏的数据,用 np.zeros((n, n)) 是灾难。5 万维词汇表的共现矩阵理论占 11.4 GB(int32),但非零元素通常 scipy.sparse.csr_matrix,内存可压到百 MB 级。关键不是“能不能算”,而是“有没有必要存零”。构造时用三元组列表(row, col, data)动态填充,最后一次性生成 CSR,避免频繁插入开销。
真正难处理的是既大又稠密、还必须做逐元素运算的场景——这时候 memmap + out= 是底线方案,但每一步的模式、切片、flush 都得卡死,漏一个就前功尽弃。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











