问题
我编写了一个神经网络分类器,可以处理大量的图像(每个图像大小约为1-3 GB),对它们进行切片,并将这些切片逐个通过网络传输。训练速度非常缓慢,因此我进行了基准测试,发现从一个图像中加载补丁到内存中需要大约50秒的时间(使用Openslide库),而仅需要0.5秒的时间将其通过模型传递。
然而,我正在一台具有1.5Tb RAM的超级计算机上工作,其中只有约26 Gb被利用。数据集总共有约500Gb。我的想法是,如果我们可以将整个数据集加载到内存中,那么它将极大地加快训练速度。但是我正在与一个研究团队合作,并且我们正在多个Python脚本中运行实验。因此,理想情况下,我希望在一个脚本中将整个数据集加载到内存中,并能够在所有脚本中访问它。
更多细节:
- 我们在单独的Docker容器中运行各自的实验(在同一台机器上),因此数据集必须可供多个容器访问。
- 数据集是Camelyon16数据集;图像以
.tif
格式存储。 - 我们只需要读取图像,不需要写入。
- 我们每次只需要访问数据集的一小部分。
可能的解决方案
我找到了许多关于如何在多个Python脚本之间共享Python对象或原始数据的帖子:
在脚本之间共享Python数据
使用多进程模块中的SyncManager和BaseManager创建服务器进程| 示例1 | 示例2 | 文档-服务器进程 | 文档-SyncManagers
- 优点:可以通过网络共享给不同计算机上的进程(它可以被多个容器共享吗?)
- 可能问题:根据文档,比使用共享内存慢。如果我们使用客户端/服务器在多个容器之间共享内存,是否比所有脚本从磁盘读取更快?
- 可能问题:根据这个答案,
Manager
对象在发送前对对象进行了pickle处理,可能会减慢速度。
- 可能存在的问题:
mmap
将文件映射到虚拟内存,而不是物理内存 - 它创建了一个临时文件。 - 可能存在的问题:因为我们一次只使用数据集的一小部分,虚拟内存会将整个数据集放在磁盘上,我们会遇到抖动问题,程序会变得缓慢。
Python的sysv_ipc模块。这个演示看起来很有前途。
- 可能存在的问题:也许只是内置的
multi-processing
模块中可用事物的较低级别曝光?
我还发现了Python中IPC /网络选项的此列表。
一些人讨论服务器-客户端设置,一些人讨论串行化/反串行化,但我担心这将比从磁盘读取更耗时。我找到的答案都没有回答我的问题,即它们是否会在I/O上产生性能提升。
在Docker容器之间共享内存
我们不仅需要在脚本之间共享Python对象/内存,还需要在Docker容器之间共享它们。
Docker 文档 很好地解释了 --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脚本之间集成共享内存。我的问题:
1:是否有任何一种方法比从磁盘中读取更快?共享跨进程/容器的数据是否能够提高性能?
2:哪种方法最适合在多个Docker容器之间共享内存中的数据?
3:如何将Python的内存共享解决方案与
docker run --ipc=<mode>
集成?(共享IPC命名空间是否是在Docker容器之间共享内存的最佳方法?)4:是否有比这些更好的解决方案来解决我们大量I/O开销的问题?
最小工作示例-更新。不需要外部依赖!这是我在单独的容器中运行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
127.0.0.1
相互通信。你可以尝试使用--network host
启动它们,也许会有所帮助。 - swenzelclient.py
中的manager.connect()
不再出现ConnectionRefusedError
,程序可以执行到image_patches = patch_dict.get(key)
,但是会引发这个错误。 - Jacob Sternipc
。请使用--network=container:cancer-1
而不是--ipc=container:cancer-1
,然后再试一下。 - Tarun Lalwani