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

如何在共享内存缓冲区上构建原地Python Dict?

问题

我正在使用multiprocessing.shared_memory在多个MPI进程间共享字典。将字典序列化为字节并复制到共享内存中的操作很简单:

import pickle
from multiprocessing import shared_memory

my_dict = {'k': 'v'}
raw_dict = pickle.dumps(my_dict)
dict_size = len(raw_dict)

# 创建共享内存
shm = shared_memory.SharedMemory(create=True, size=dict_size)
shm_name = shm.name
shm.buf[:dict_size] = raw_dict

但如何无需复制内存将其转换回字典却让人困惑:

# 通过名称获取已创建的共享内存
shm = shared_memory.SharedMemory(name=shm_name)
# 这段代码无法运行
deserialized_dict = dict(shm.buf) 

请问Python中是否存在在缓冲区上原地构建对象的方法?

以下是完整代码供参考:

import numpy as np
from mpi4py import MPI
from multiprocessing import shared_memory, resource_tracker


comm = MPI.COMM_WORLD
rank = comm.Get_rank()
size = comm.Get_size()

if rank == 0:
    my_dict = {'k': 'v'}
    raw_dict = pickle.dumps(my_dict)
    dict_size = len(raw_dict)

    shm = shared_memory.SharedMemory(create=True, size=dict_size)
    shm.buf[:dict_size] = raw_dict
    comm.bcast(shm.name, root=0)
else:
    shm_name = comm.bcast(None, root=0)
    print(rank, shm_name)
    shm = shared_memory.SharedMemory(name=shm_name)
    # 无法运行的代码
    deserialized_dict = dict(shm.buf)
    # 这段代码会创建新内存来构建字典
    # deserialized_dict = pickle.loads(shm.buf)
    print(rank, deserialized_dict)

comm.Barrier()

if rank == 0:
    shm.close()
    shm.unlink()
else:
    shm.close()
    resource_tracker.unregister(shm._name, 'shared_memory')
解决方案

首先明确:Python原生dict无法直接在共享内存缓冲区上原地构建,核心原因有两点:

  1. Python内置对象(包括dict)的内存布局由解释器管控,依赖Python私有堆内存,无法直接映射到外部共享内存区域。
  2. 所有反序列化方法(如pickle.loads)本质上都会在进程私有内存中重建对象,必然会复制共享内存中的字节数据——这是Python对象模型决定的,无法完全规避。

不过可以通过两种思路优化你的需求:

1. 使用共享内存原生的类字典结构

如果允许替换原生dict,可以直接用基于共享内存实现的类字典工具,避免序列化/反序列化的复制开销:

  • 第三方库如shared_dict:直接在共享内存中维护键值对结构,进程间访问无需复制数据。

示例代码:

# 进程0创建共享字典
from shared_dict import SharedDict
sh_dict = SharedDict(create=True, name='my_shared_dict')
sh_dict['k'] = 'v'

# 其他进程直接访问共享字典
sh_dict = SharedDict(name='my_shared_dict')
print(sh_dict['k'])  # 直接读取共享内存数据,无额外复制

2. 最小化序列化的复制开销

如果必须使用原生dict,可以通过以下方式降低复制成本:

  • 用更高效的序列化库(如msgpack、ujson)替代pickle,它们生成的序列化结果更小,复制开销更低。
  • 直接传递memoryview切片给反序列化函数:shm.buf是memoryview对象,pickle.loads可以直接读取它,无需先转换为bytes(这已经是当前能做到的最小复制方式)。

修改后的反序列化代码:

shm = shared_memory.SharedMemory(name=shm_name)
# 直接用memoryview切片反序列化,避免额外内存复制
deserialized_dict = pickle.loads(shm.buf[:dict_size])

注:这里的"无额外复制"指的是跳过了memoryview转bytes的步骤,但创建dict对象本身需要在私有内存中分配空间存储键值对,这是Python对象模型无法避免的。

为什么dict(shm.buf)无法运行

shm.buf是memoryview类型,dict()构造函数需要的是可迭代的键值对,而非字节数据,这段代码本身就不符合语法逻辑,自然无法执行。

内容的提问来源于Stack Exchange,提问作者Rossil

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 18:23:11