问题
我编写了一个神经网络分类器,它接收大量图像(每张约 1-3 GB),将它们拼凑起来,然后将这些拼块单独通过网络。训练进行得非常缓慢,所以我对它进行了基准测试,发现将一个图像中的补丁加载到内存中需要大约 50 秒(使用 Openslide library),而将它们传递到模型中只需要大约 0.5 秒。
但是,我正在开发一台具有 1.5Tb RAM 的 super 计算机,其中仅使用了 ~26 Gb。数据集总共约 500Gb。我的想法是,如果我们可以将整个数据集加载到内存中,它将极大地加快训练速度。但我正在与一个研究团队合作,我们正在对多个 Python 脚本进行实验。所以理想情况下,我想在一个脚本中将整个数据集加载到内存中,并能够跨所有脚本访问它。
更多细节:
.tif
格式。 可能的解决方案
我发现了很多关于如何跨多个 Python 脚本共享内存中的 Python 对象或原始数据的帖子:
跨脚本共享 Python 数据
多处理模块中带有 SyncManager 和 BaseManager 的服务器进程 | Example 1 | Example 2 | Docs - Server Processes | Docs - SyncManagers
Manager
object 在发送对象之前先腌制对象,这可能会减慢速度。 mmap模块 | Docs
mmap
将文件映射到 virtual memory, not physical memory - 它创建一个临时文件。 Pyro4 (Python 对象的客户端-服务器) | Docs
sysv_ipc Python 模块。 This demo看起来很有希望。
multi-processing
模块? 我还找到了 this list Python 中 IPC/网络的选项。
有些讨论服务器-客户端设置,有些讨论序列化/反序列化,恐怕这比从磁盘读取需要更长的时间。我找到的答案都没有解决我关于这些是否会导致 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 问题( 1 、 2 、 3 、 4 )没有解决单独 Docker 容器中 Python 脚本之间共享内存的集成问题。我的问题:
docker run --ipc=<mode>
集成? (共享 IPC 命名空间甚至是跨 docker 容器共享内存的最佳方式吗?)最小工作示例 - 更新。不需要外部依赖!
这是我在不同容器中的 Python 脚本之间共享内存的幼稚方法。当 Python 脚本在同一个容器中运行时它起作用,但当它们在不同的容器中运行时不起作用。
server.py
from multiprocessing.managers import SyncManager
import multiprocessing
patch_dict = {}
image_level = 2
image_files = ['path/to/normal_042.tif']
region_list = [(14336, 10752),
(9408, 18368),
(8064, 25536),
(16128, 14336)]
def load_patch_dict():
for i, image_file in enumerate(image_files):
# We would load the image files here. As a placeholder, we just add `1` to the dict
patches = 1
patch_dict.update({'image_{}'.format(i): patches})
def get_patch_dict():
return patch_dict
class MyManager(SyncManager):
pass
if __name__ == "__main__":
load_patch_dict()
port_num = 4343
MyManager.register("patch_dict", get_patch_dict)
manager = MyManager(("127.0.0.1", port_num), authkey=b"password")
# Set the authkey because it doesn't set properly when we initialize MyManager
multiprocessing.current_process().authkey = b"password"
manager.start()
input("Press any key to kill server".center(50, "-"))
manager.shutdown
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
最佳答案
我建议您尝试使用 tmpfs .
它是一个 linux 特性,允许您创建一个虚拟文件系统,所有这些都存储在 RAM 中。这允许非常快速的文件访问,并且只需一个 bash 命令即可设置。
除了非常快速和直接之外,它在您的情况下还有许多优点:
cp
将数据集放入 tmpfs
tmpfs
可以适应和交换页面到硬盘驱动器。如果您必须在没有可用 RAM 的服务器上运行它,您可以将所有文件都放在带有普通文件系统的硬盘驱动器上,而根本不接触您的代码。 使用步骤:
sudo mount -t tmpfs -o size=600G tmpfs /mnt/mytmpfs
cp -r dataset /mnt/mytmpfs
编辑:
ramfs
可能比 tmpfs
快在某些情况下,因为它不实现页面交换。要使用它只需替换 tmpfs
与 ramfs
在上面的说明中。
关于python - IPC 在单独的 Docker 容器中跨 Python 脚本共享内存,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56908476/