准确确定 Python 多处理过程中腌制的内容

Determining exactly what is pickled during Python multiprocessing

如线程 What is being pickled when I call multiprocessing.Process? 中所述,在某些情况下,多处理几乎不需要通过酸洗传输数据。例如,在 Unix 系统上,解释器使用 fork() 创建进程,并且 multiprocessing 启动时已经存在的对象可以被每个进程访问而无需 pickling。

但是,我正在尝试考虑“这就是它应该如何工作”之外的场景。例如,代码可能有一个错误,一个应该是只读的对象被无意中修改,导致它的酸洗被转移到其他进程。

有什么方法可以确定在多处理过程中腌制了什么,或者至少腌制了多少?这些信息不一定是实时的,但如果有一种方法可以获得一些统计数据(例如,腌制的对象数量)这可能会有所帮助,这可能会提示为什么某些事情需要更长的时间 运行 超出预期,因为意外的酸洗开销。

我正在寻找 Python 环境的内部解决方案。进程跟踪(例如 Linux strace)、网络侦听、广义 IPC 统计以及可能用于计算进程之间移动的字节数的类似解决方案不够具体,无法识别对象酸洗与其他类型的通信。


已更新:令人失望的是,除了破解模块 and/or 解释器源代码之外,似乎无法收集酸洗统计数据。但是,@aaron 确实解释了这一点,并澄清了一些小问题,所以我接受了这个答案。

Multiprocessing 并不是一个简单的库,但是一旦您熟悉了它的工作原理,就很容易四处寻找并弄清楚它。

您通常希望从 context.py 开始。这是所有有用的 类 根据 OS 绑定的地方,并且......好吧......你激活的“上下文”。有 4 个基本上下文:posix 的 Fork、ForkServer 和 Spawn;和 windows 的单独 Spawn。它们又各自有自己的“Popen”(在 start() 调用)来启动一个新进程来处理单独的实现。

popen_fork.py

创建一个进程字面上调用 os.fork(),然后在子进程中组织到 运行 BaseProcess._bootstrap() 设置一些清理内容然后调用 self.run() 来执行代码你给它。 没有 pickling 发生 以这种方式启动进程,因为整个内存 space 被复制(有一些例外。参见:fork(2)).

popen_spawn_xxxxx.py

我最熟悉 windows,但我认为 win32 和 posix 版本的操作方式非常相似。一个新的 python 进程是用一个简单的精心制作的命令行字符串创建的,其中包括一对指向 read/write from/to 的管道句柄。新进程将导入 __main__ 模块(通常等于 sys.argv[0])以便访问所有需要的引用。然后它将执行一个简单的 bootstrap 函数(来自命令字符串),该函数尝试读取和 un-pickle 一个 Process 对象 从它的管道中创建它。一旦它有了 Process 实例(一个新对象,它是一个副本;而不仅仅是对原始对象的引用),它将再次安排调用 _bootstrap().

popen_forkserver.py

第一次使用“forkserver”上下文创建新进程时,新进程将“生成”运行一个简单的服务器(监听管道)来处理新的进程请求。随后的流程请求都转到同一台服务器(基于导入机制和服务器实例的模块级全局)。然后从该服务器“分叉”新进程,以节省启动新 python 实例的时间。然而,这些新进程不能有任何相同的(如在同一对象中而不是副本中)Process 对象,因为它们从中派生的 python 进程本身就是“生成”的。因此 Process 实例被 pickle 并像使用“spawn” 一样发送。此方法的好处包括: 进行分叉的进程是单线程的,以避免死锁。启动新 python 口译员的费用只需支付一次。解释器的内存消耗以及 __main__ 导入的任何模块都可以在很大程度上共享,因为“fork”通常使用写时复制内存页。


在所有情况下,一旦发生拆分,您应该认为内存 space 是完全独立的,它们之间的唯一通信是通过管道或共享内存。锁和信号量由一个扩展库(用 c 编写)处理,但基本上是由 OS 管理的命名信号量。 QueuePipemultiprocessing.Manager使用pickling来同步对他们 return 的代理对象。新的 multiprocessing.shared_memory 使用内存映射文件或缓冲区来共享数据(由 OS 之类的信号量管理)。

解决您的问题:

the code may have a bug and an object which is supposed to read-only is inadvertently modified, leading to its pickling to be transferred to other processes.

这仅适用于 multiprocessing.Manager 代理对象。由于其他所有事情都要求您非常有意识地 发送ing 和 接收ing 数据,或者使用除 pickling 之外的其他传输机制。