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

如何基于现有NumPy数组初始化共享内存,实现子进程无拷贝访问

无需复制大NumPy数组实现跨进程访问的方案

这个问题我太有共鸣了——GB级的大数组要是复制一次,不仅耗时还占满内存,完全是得不偿失。下面分场景给你最实用的解决方案:

一、Unix系统(Linux/macOS):利用fork的写时复制特性(零复制)

Unix下multiprocessing默认用fork启动子进程,这种方式下子进程会共享父进程的内存空间,而且采用**写时复制(COW)**机制:只有当某一方修改内存页时,系统才会复制该页数据,纯读操作完全不会复制整个数组。

直接上代码:

import multiprocessing as mp
import numpy as np

def process_task(arr):
    # 子进程直接访问数组,纯读操作不会触发复制
    print("子进程读取数组前5个元素:", arr[:5])
    # 如果要修改:注意这里会触发对应内存页的复制,但依然不会复制整个数组
    # arr[0] = 999

if __name__ == "__main__":
    # 模拟超大NumPy数组(比如1亿个float64元素,约800MB)
    big_array = np.random.rand(10**8)
    
    # 启动子进程,直接传递数组
    proc = mp.Process(target=process_task, args=(big_array,))
    proc.start()
    proc.join()

这种方法最简单,而且完全零复制,是Unix环境下的最优解。

二、跨平台方案(含Windows):用共享内存直接映射

Windows不支持fork,默认用spawn启动子进程,会序列化传递的对象,导致数组被完整复制。这时候就需要用multiprocessing.shared_memory(Python3.8+新增)来创建共享内存块,让子进程直接映射这块内存。

注意:如果数组已经存在,必须做一次复制到共享内存(唯一的一次开销);如果可以提前规划,最好直接在共享内存中创建数组,彻底避免复制:

import multiprocessing as mp
from multiprocessing import shared_memory
import numpy as np

def process_task(shm_name, arr_shape, arr_dtype):
    # 连接到父进程创建的共享内存块
    existing_shm = shared_memory.SharedMemory(name=shm_name)
    # 从共享内存直接构建NumPy数组,无复制
    shared_arr = np.ndarray(arr_shape, dtype=arr_dtype, buffer=existing_shm.buf)
    
    print("子进程读取数组前5个元素:", shared_arr[:5])
    
    # 用完关闭共享内存(不要unlink,父进程负责释放)
    existing_shm.close()

if __name__ == "__main__":
    # 方案1:直接在共享内存中创建数组(零复制)
    arr_shape = (10**8,)
    arr_dtype = np.float64
    # 创建共享内存块,大小匹配数组字节数
    shm = shared_memory.SharedMemory(create=True, size=arr_shape[0] * arr_dtype.itemsize)
    # 基于共享内存构建NumPy数组
    big_array = np.ndarray(arr_shape, dtype=arr_dtype, buffer=shm.buf)
    # 可以正常给数组赋值,数据直接存在共享内存中
    big_array[:] = np.random.rand(*arr_shape)

    # 方案2:如果数组已经存在,需要复制到共享内存(仅一次开销)
    # big_array = np.random.rand(10**8)
    # shm = shared_memory.SharedMemory(create=True, size=big_array.nbytes)
    # shared_arr = np.ndarray(big_array.shape, dtype=big_array.dtype, buffer=shm.buf)
    # shared_arr[:] = big_array[:]  # 这里会复制一次数据

    # 启动子进程,传递共享内存名称、数组形状和类型
    proc = mp.Process(target=process_task, args=(shm.name, arr_shape, arr_dtype))
    proc.start()
    proc.join()

    # 父进程最后关闭并释放共享内存
    shm.close()
    shm.unlink()

三、旧版本Python(3.8以下):用multiprocessing.Array映射

如果你的Python版本低于3.8,可以用multiprocessing.Array创建共享内存数组,再用np.frombuffer将其转换为NumPy数组(无复制):

import multiprocessing as mp
import numpy as np

def process_task(shared_arr, arr_shape):
    # 从共享内存数组映射为NumPy数组,无复制
    np_arr = np.frombuffer(shared_arr.get_obj(), dtype=np.float64).reshape(arr_shape)
    print("子进程读取数组前5个元素:", np_arr[:5])

if __name__ == "__main__":
    arr_shape = (10**8,)
    # 创建共享内存数组,'d'对应float64类型
    shared_arr = mp.Array('d', arr_shape[0])
    # 转换为NumPy数组并赋值
    np_arr = np.frombuffer(shared_arr.get_obj(), dtype=np.float64).reshape(arr_shape)
    np_arr[:] = np.random.rand(*arr_shape)

    proc = mp.Process(target=process_task, args=(shared_arr, arr_shape))
    proc.start()
    proc.join()

关键提醒

  • Unix下优先用fork方案,零复制效率最高;
  • Windows下无法避免首次复制(除非提前在共享内存创建数组);
  • 如果子进程需要修改数组,必须用共享内存方案(fork的写时复制会导致父子进程数据分离)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 04:19:39