You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何高效将多维NumPy数组分写入多文件?(附多进程尝试反馈)

批量保存NumPy图像数组的高效玩法

先搞懂为啥你之前的并行反而更慢

  • 磁盘IO才是最大的坑:10000张750×750的图,单张按3通道uint8算就6.3MB,总数据量60GB往上。机械硬盘的随机写入本来就拉胯,多进程/线程同时写会让磁盘磁头来回跳,反而拖慢速度;SSD虽然好点,但并发数太高也会触发IO瓶颈。
  • 进程池的额外开销:用concurrent.futures.ProcessPoolExecutor时,每个子进程都要复制你要存的图像切片,大数组的拷贝成本比并行省的时间还多,当然越跑越慢。

靠谱的优化方案

1. 先把单线程的效率拉满

  • 选对存储格式:别用PIL存PNG/JPG(压缩太耗时间),如果不需要直接看图,用numpy.save存npy格式最快;要是必须存可视化格式,就存未压缩的BMP,比PNG快好几倍。
    代码示例:
    import numpy as np
    from PIL import Image
    
    # 假设arr是你的(i, 750, 750, z)数组
    arr = np.random.rand(10000, 750, 750, 3).astype(np.uint8)
    
    # 最快的方式:存npy格式
    for idx in range(arr.shape[0]):
        np.save(f"img_{idx}.npy", arr[idx])
    
    # 要可视化的话存未压缩BMP
    for idx in range(arr.shape[0]):
        img = Image.fromarray(arr[idx])
        img.save(f"img_{idx}.bmp", format="BMP")
    
  • 先写大文件再拆分:把整个数组先一次性写入一个二进制文件,然后用系统的split工具拆分,避开Python循环的IO开销,适合不需要实时处理的场景。

2. 非要并行?控制并发数+避免数据拷贝

  • 用共享内存存大数组:把原始数组放到共享内存里,子进程直接读,不用每次拷贝切片,这能省掉一大笔开销。同时把并发数控制在2-4个(机械硬盘就2个,SSD可以到4个),别让磁盘忙不过来。
    代码示例:
    import numpy as np
    from multiprocessing import Pool, Array
    from PIL import Image
    
    def save_single(args):
        idx, shared_buf, x, y, z = args
        # 从共享内存里取出对应图像
        img_arr = np.frombuffer(shared_buf, dtype=np.uint8).reshape((-1, x, y, z))[idx]
        img = Image.fromarray(img_arr)
        img.save(f"img_{idx}.bmp")
    
    if __name__ == "__main__":
        arr = np.random.rand(10000, 750, 750, 3).astype(np.uint8)
        x, y, z = arr.shape[1:]
        # 创建共享内存缓冲区
        shared_buf = Array('B', arr.size)
        # 把数据拷贝到共享内存
        np.copyto(np.frombuffer(shared_buf.get_obj(), dtype=np.uint8), arr.flatten())
        # 控制进程数为4
        with Pool(processes=4) as pool:
            pool.map(save_single, [(i, shared_buf, x, y, z) for i in range(arr.shape[0])])
    
  • 用线程池代替进程池:线程共享内存,没有数据拷贝的问题,同样控制并发数,开销比进程池小很多。

3. 用专门的工具库省心

  • Dask:自动帮你分块、调度并行任务,不用自己管并发数,能避免IO过载。
    代码示例:
    import dask.array as da
    import numpy as np
    from PIL import Image
    
    arr = np.random.rand(10000, 750, 750, 3).astype(np.uint8)
    # 按100张图为一块分块
    dask_arr = da.from_array(arr, chunks=(100, 750, 750, 3))
    
    def save_chunk(chunk):
        for idx, img_arr in enumerate(chunk):
            img = Image.fromarray(img_arr)
            img.save(f"img_{idx}.bmp")
    
    # 执行并行保存
    dask_arr.map_blocks(save_chunk, dtype=da.void()).compute()
    
  • Zarr:适合存分块数组,后续要读取单个图像也方便,批量导出也很顺手。

额外小技巧

  • 机械硬盘的话,把文件写到连续分区,减少磁头来回跑的时间。
  • 提前建好输出目录,别让程序每次存文件时都创建目录。
  • 能降精度就降:比如把float32转成uint8,文件大小直接砍到1/4,写入速度自然快。

内容的提问来源于stack exchange,提问作者alwayscurious

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 19:55:18