簡體   English   中英

IPC在單獨的Docker容器中的Python腳本之間共享內存

[英]IPC shared memory across Python scripts in separate Docker containers

問題

我已經編寫了一個神經網絡分類器,該分類器可以獲取海量圖像(每張圖像約1-3 GB),將其打補丁,然后分別通過網絡傳遞這些補丁。 培訓的進行過程非常緩慢,因此我對其進行了基准測試,發現用大約50秒的時間將補丁從一個圖像加載到內存(使用Openslide庫 ),而僅需0.5秒的時間就可以將它們通過模型。

但是,我正在使用具有1.5Tb RAM的超級計算機,其中僅使用了約26 Gb。 數據集總計約500Gb。 我的想法是,如果我們可以將整個數據集加載到內存中,它將極大地加快訓練速度。 但是我正在與一個研究團隊合作,我們正在多個Python腳本之間進行實驗。 因此,理想情況下,我想將一個腳本中的整個數據集加載到內存中,並能夠在所有腳本中對其進行訪問。

更多細節:

  • 我們在單獨的Docker容器中(在同一台機器上)運行各個實驗,因此必須跨多個容器訪問數據集。
  • 數據集為Camelyon16數據集 圖像以.tif格式存儲。
  • 我們只需要閱讀圖像,而無需編寫。
  • 我們只需要一次訪問數據集的一小部分。

可能的解決方案

我發現了很多關於如何在多個Python腳本之間共享Python對象或內存中的原始數據的文章:

跨腳本共享Python數據

多處理模塊中具有SyncManager和BaseManager的服務器進程| 示例1 | 示例2 | Docs-服務器進程 | 文件-SyncManager

  • 肯定:可以由網絡上不同計算機上的進程共享(可以由多個容器共享嗎?)
  • 可能的問題:根據文檔顯示,速度比使用共享內存慢。 如果我們使用客戶端/服務器在多個容器之間共享內存,那會比從磁盤讀取所有腳本的速度快嗎?
  • 可能的問題:根據此答案Manager對象在發送對象之前先對其進行腌制,這可能會使速度變慢。

mmap模塊| 文件

  • 可能的問題: mmap將文件映射到虛擬內存,而不是物理內存 -它創建一個臨時文件。
  • 可能的問題:因為我們在同一時間只能使用數據集的一小部分,虛擬內存使整個數據集在磁盤上,我們遇到顛簸問題和程序slogs。

Pyro4 (Python對象的客戶端服務器)| 文件

適用於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問題( 1234 )不以單獨的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

我建議您嘗試使用tmpfs

它是一項Linux功能,可讓您創建一個虛擬文件系統,所有文件系統都存儲在RAM中。 這樣可以非常快速地訪問文件,並且只需設置一個bash命令即可。

除了非常快速和直接之外,它在您的情況下還具有許多優點:

  • 無需觸摸當前代碼-數據集的結構保持不變
  • 創建共享數據集無需額外的工作-只需將數據集cptmpfs
  • 通用接口-作為文件系統,您可以輕松地將RAM數據集與系統中不一定要用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

我認為shared memorymmap解決方案是正確的。

共享內存:

在服務器進程中首先讀取內存中的數據集。 對於python,只需使用multiprocessing包裝器在進程之間的共享內存中創建對象,例如: multiprocessing.Valuemultiprocessing.Array ,然后創建Process並將共享對象作為args傳遞。

mmap:

將數據集存儲在主機上的文件中。 然后,每個容器將文件安裝到容器中。 如果一個容器打開文件並將其映射到其虛擬內存,則另一個容器在打開文件時將不需要從磁盤讀取文件到內存,因為該文件已在物理內存中。

PS我不確定cpython如何在進程之間實現大共享內存,大概cpython共享內存使用mmap內部。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM