joblib.dump 不支持 mmap_mode 参数,该参数仅对 joblib.load 有效;正确用法是 dump 时不传,load 时指定 ‘r’ 或 ‘c’ 以实现大数组内存映射。

joblib.dump 用 mmap_mode='r' 为什么反而报错?
因为 mmap_mode 只对 joblib.load 生效,dump 时传了也直接忽略。常见错误是看到文档里写了 mmap_mode,就顺手塞进 dump 调用里,结果既没效果,还可能掩盖真正问题。
真正起作用的场景是:先用 joblib.dump(obj, 'data.pkl') 存好,之后在 load 时加 mmap_mode='r' 或 'c',让大数组不全载入内存。
-
mmap_mode='r':只读映射,适合只读分析,进程退出后自动释放 -
mmap_mode='c':copy-on-write,允许修改但不写回磁盘,适合临时计算 - 如果文件被其他进程锁住、或路径不存在、或权限不足,
load会抛OSError: [errno 22] Invalid argument - Windows 上对 mmap 支持较弱,尤其 NTFS 压缩文件夹或 onedrive 同步目录里,容易触发
ValueError: mmap Length is greater than file size
哪些对象能从 mmap_mode 真正受益?
只有内部含大型 numpy.ndarray(且 dtype 是基础类型如 float64、int32)的对象才走内存映射路径。普通 dict、list、pandas DataFrame(底层虽用 ndarray,但 joblib 默认不 mmap 其 block)、自定义类实例——统统不映射,还是全量反序列化。
验证方法:用 joblib.load('x.pkl', mmap_mode='r') 加载后,检查关键数组的 .base 是否为 mmap.mmap 实例:
立即学习“Python免费学习笔记(深入)”;
import numpy as np arr = joblib.load('big_array.pkl', mmap_mode='r') print(type(arr.base)) # 应该是 <class 'mmap.mmap'>
- 小数组(默认小于 1MB)即使指定
mmap_mode也不会映射,joblib 认为不值得开销 - 如果数组是 view(比如
a[::2]),则.base指向原 mmap,但自身仍算“映射数组” - 混用不同 dtype 的结构化数组(如
np.dtype([('x', 'f4'), ('y', 'i8')]))可能 fallback 到普通加载
多进程共享同一个 joblib 文件,mmap_mode='r' 安全吗?
安全,前提是所有进程都只读。多个进程同时以 mmap_mode='r' 打开同一文件,底层共用同一段物理内存页,零拷贝、低延迟、省内存。
但要注意:
- 不能有任一进程用
mmap_mode='r+'或'w+',否则触发写时复制(COW)或直接报错 - 文件不能被外部程序(如文本编辑器、ide)意外修改,否则 mmap 区域内容会突变,导致静默计算错误
- linux 下若文件被
truncate缩小,已映射区域访问会触发signal 7 (SIGBUS),python 进程直接崩溃,无 traceback - joblib 不做文件锁,靠使用者保证“写完再读”,推荐用原子重命名(
os.replace())更新文件
替代方案:什么时候该放弃 joblib mmap,改用 np.memmap?
当你要精细控制数据布局、需要随机访问子区间、或必须跨语言(如 C/Fortran)读取时,joblib 的封装反而碍事。它把 mmap 隐藏在 pickle 流里,没法跳过 header 直接寻址。
例如处理 TB 级遥感影像切片,常用做法是:
arr = np.memmap('data.bin', dtype='float32', mode='r', shape=(10000, 10000)) tile = arr[1000:1100, 2000:2100] # 零拷贝切片
-
np.memmap明确分离“文件布局”和“逻辑视图”,调试时可直接用hexdump查看二进制 - joblib 的 mmap 是 pickle 协议的一部分,无法用非 Python 工具解析
- 如果你的 pipeline 里已有大量
np.memmap逻辑,硬套 joblib 反而增加序列化/反序列化开销
复杂点在于:joblib 的 mmap 是“透明”的,你几乎感觉不到;而 np.memmap 要自己管 shape、dtype、offset,错一点就 segfault。别为了省几行代码,把内存地址当玩具玩。