IPC在不同的Docker容器中跨Python脚本共享内存

2024-05-08 02:58:44 发布

您现在位置:Python中文网/ 问答频道 /正文

问题

我已经编写了一个神经网络分类器,它接收大量图像(每个图像约1-3gb),对它们进行修补,然后将这些补丁分别通过网络。训练进行得很慢,所以我对它进行了基准测试,发现从一个图像将补丁加载到内存中需要大约50秒(使用Openslide library),而通过模型只需要大约0.5秒。在

然而,我正在研制一台拥有1.5Tb内存的超级计算机,其中只有26GB的内存被利用。数据集总计约500Gb。我的想法是,如果我们能够将整个数据集加载到内存中,它将大大加快训练速度。但是我和一个研究团队合作,我们正在多个Python脚本上进行实验。所以理想情况下,我希望在一个脚本中将整个数据集加载到内存中,并能够跨所有脚本访问它。在

更多详情:

  • 我们在不同的Docker容器(在同一台机器上)中运行我们的单独实验,因此数据集必须可以跨多个容器访问。在
  • 数据集是Camelyon16 dataset;图像以.tif格式存储。在
  • 我们只需要读图像,不需要写。在
  • 我们一次只需要访问数据集的一小部分。在

可能的解决方案

我发现了很多关于如何在多个Python脚本之间共享Python对象或内存中的原始数据的帖子:

跨脚本共享Python数据

在多处理模块| Example 1| Example 2| Docs - Server Processes| Docs - SyncManagers中使用SyncManager和BaseManager的服务器进程

  • 优点:可以通过网络由不同计算机上的进程共享(是否可以由多个容器共享?)在
  • 可能的问题:根据文件,比使用共享内存慢。如果我们使用一个客户机/服务器跨多个容器共享内存,这会比从磁盘读取的所有脚本快吗?在
  • 可能的问题:根据this answerManager对象在发送对象之前对其进行pickle处理,这可能会降低速度。在

mmap模块| Docs

  • 可能的问题:mmap将文件映射到virtual memory, not physical memory-它创建了一个临时文件。在
  • 可能的问题:因为我们一次只使用数据集的一小部分,虚拟内存将整个数据集放在磁盘上,因此我们遇到了thrashing问题和程序问题。在

Pyro4(Python对象的客户机服务器)| Docs

Python的sysv_ipc模块。This demo看起来很有前途。在

我还发现了Python中IPC/networking的this list选项。在

有些讨论服务器-客户机设置,有些讨论序列化/反序列化,这恐怕比从磁盘读取要花更长的时间。我找到的答案中没有一个能解决我的问题,即这些问题是否会导致I/O性能的提高

跨Docker容器共享内存

我们不仅需要跨脚本共享Python对象/内存,还需要跨Docker容器共享它们。在

Docker documentation很好地解释了--ipc标志。对我来说,有意义的是:

docker run -d --ipc=shareable data-server
docker run -d --ipc=container:data-server data-client

但是,当我使用如上所述设置的--ipc连接在不同的容器中运行客户机和服务器时,它们无法相互通信。我读过的SO问题(1234)没有解决在不同的Docker容器中Python脚本之间共享内存的集成问题。在

我的问题:

  • 1: 它们中的任何一个都能提供比从磁盘读取更快的访问速度吗?认为跨进程/容器共享内存中的数据可以提高性能是否合理?在
  • 2: 对于跨多个docker容器共享内存中的数据,哪种解决方案最合适?在
  • 3: 何如何将Python的内存共享解决方案与docker run --ipc=<mode>集成?(共享的IPC名称空间是在docker容器之间共享内存的最佳方式吗?)在
  • 4: 有没有比这些更好的解决方案来解决大I/O开销的问题?在

最小工作示例-更新。不需要外部依赖项!在

这是我在不同容器中的Python脚本之间共享内存的天真方法。当Python脚本运行在同一个容器中时,它是有效的,但当它们在不同的容器中运行时,它就不起作用了。在

server.py

^{pr2}$

client.py

from multiprocessing.managers import SyncManager
import multiprocessing
import sys, time

class MyManager(SyncManager):
    pass

MyManager.register("patch_dict")

if __name__ == "__main__":
    port_num = 4343

    manager = MyManager(("127.0.0.1", port_num), authkey=b"password")
    multiprocessing.current_process().authkey = b"password"
    manager.connect()
    patch_dict = manager.patch_dict()

    keys = list(patch_dict.keys())
    for key in keys:
        image_patches = patch_dict.get(key)
        # Do NN stuff (irrelevant)

当脚本在同一个容器中运行时,这些脚本可以很好地共享图像。但当它们在不同的容器中运行时,如下图所示:

# Run the container for the server
docker run -it --name cancer-1 --rm --cpus=10 --ipc=shareable cancer-env
# Run the container for the client
docker run -it --name cancer-2 --rm --cpus=10 --ipc=container:cancer-1 cancer-env

我得到以下错误:

Traceback (most recent call last):
  File "patch_client.py", line 22, in <module>
    manager.connect()
  File "/usr/lib/python3.5/multiprocessing/managers.py", line 455, in connect
    conn = Client(self._address, authkey=self._authkey)
  File "/usr/lib/python3.5/multiprocessing/connection.py", line 487, in Client
    c = SocketClient(address)
  File "/usr/lib/python3.5/multiprocessing/connection.py", line 614, in SocketClient
    s.connect(address)
ConnectionRefusedError: [Errno 111] Connection refused

Tags: 数据对象内存dockerrunpy图像脚本
2条回答

我认为shared memory或{}的解决方案是合适的。在

共享内存:

第一次读取服务器进程内存中的数据集。对于python,只需使用multiprocessing包装器在进程之间的共享内存中创建对象,例如:multiprocessing.Value或{a2},然后创建进程并将共享对象作为参数传递。在

mmap:

将数据集存储在主机上的文件中。然后每个容器将文件装入容器中。如果一个容器打开该文件并将其映射到其虚拟内存中,那么当打开该文件时,另一个容器将不需要将该文件从磁盘读取到内存,因为该文件已经在物理内存中。在

另外,我不确定cpython如何实现进程之间的大共享内存,可能cpython共享内存使用mmap内部。在

我建议您尝试使用tmpfs。在

它是一个linux特性,允许您创建一个虚拟文件系统,所有文件都存储在RAM中。这允许非常快速的文件访问,只需要一个bash命令即可设置。在

除了非常快速和直截了当之外,它在您的案例中还有许多优势:

  • 不需要接触当前代码-数据集的结构保持不变
  • 创建共享数据集不需要额外的工作-只需cp将数据集放入tmpfs
  • 通用接口——作为一个文件系统,你可以很容易地将内存上的数据集与系统中不需要用python编写的其他组件集成在一起。例如,在你的容器内使用它很容易,只需将挂载目录传递到容器中即可。在
  • 将适合其他环境-如果您的代码必须在另一台服务器上运行,tmpfs可以调整并交换页面到硬盘驱动器。如果你必须在没有空闲RAM的服务器上运行这个程序,你可以把你所有的文件都放在一个普通的文件系统的硬盘上,而不需要接触你的代码。在

使用步骤:

  1. 创建一个tmpfs-sudo mount -t tmpfs -o size=600G tmpfs /mnt/mytmpfs
  2. 复制数据集-cp -r dataset /mnt/mytmpfs
  3. 将当前数据集的所有引用更改为新数据集
  4. 享受


编辑:

在某些情况下,ramfs可能比tmpfs快,因为它不实现页交换。要使用它,只需在上面的说明中将tmpfs替换为ramfs。在

相关问题 更多 >