簡體   English   中英

在基於三重奏的 Python 應用程序中生成進程並在進程之間進行通信

[英]Spawn processes and communicate between processes in a trio based Python application

對於 Python 庫flowimage的實習,我們正在研究使用庫trio編寫具有客戶端/服務器模型的 HPC 並行應用程序是否是一個好主意。

對於異步編程和 i/o,trio 確實很棒!

然后,我想知道如何

  1. spawn 進程(執行 CPU-GPU 有界工作的服務器)
  2. 在進程之間通信復雜的 Python 對象(可能包含大型 numpy 數組)。

我沒有在文檔中找到使用 trio 執行此操作的推薦方法(即使echo 客戶端/服務器教程是一個好的開始)。

在 Python 中生成進程並進行通信的一種明顯方法是使用multiprocessing

在 HPC 環境中,我認為一個好的解決方案是使用 MPI ( http://mpi4py.readthedocs.io/en/stable/overview.html#dynamic-process-management )。 作為參考,我還必須提到rpychttps://rpyc.readthedocs.io/en/latest/docs/zerodeploy.html#zerodeploy )。

我不知道是否可以將這些工具與 trio 一起使用,以及這樣做的正確方法是什么。

一個有趣的相關問題

備注PEP 574

在我看來, PEP 574 (參見https://pypi.org/project/pickle5/ )也可以成為解決這個問題的好方法的一部分。

不幸的是,截至今天(2018 年 7 月),Trio 尚不支持子進程的生成和通信,也不支持 MPI 或其他高級進程間協調協議的任何類型的高級包裝器。

這絕對是我們最終想要解決的問題,如果您想更詳細地討論需要實現的內容,那么您可以加入我們的聊天,或者本期概述了核心子流程支持所需的內容。 但如果你的目標是在幾個月內為你的實習工作,老實說你可能需要考慮更成熟的 HPC 工具,如dask

截至 2018 年年中,Trio 還沒有這樣做。 迄今為止,您最好的選擇是使用trio_asyncio來利用 asyncio 對 Trio 仍需要學習的功能的支持。

我發布了一個使用 multiprocessing 和 trio(在主程序和服務器中)的非常簡單的代碼示例。 它似乎工作。

from multiprocessing import Process, Queue
import trio
import numpy as np

async def sleep():
    print("enter sleep")
    await trio.sleep(0.2)
    print("end sleep")

def cpu_bounded_task(input_data):
    result = input_data.copy()
    for i in range(1000000-1):
        result += input_data
    return result

def server(q_c2s, q_s2c):
    async def main_server():
        # get the data to be processed
        input_data = await trio.run_sync_in_worker_thread(q_c2s.get)
        print("in server: input_data received", input_data)
        # a CPU-bounded task
        result = cpu_bounded_task(input_data)
        print("in server: sending back the answer", result)
        await trio.run_sync_in_worker_thread(q_s2c.put, result)

    trio.run(main_server)

async def client(q_c2s, q_s2c):
    input_data = np.arange(10)
    print("in client: sending the input_data", input_data)
    await trio.run_sync_in_worker_thread(q_c2s.put, input_data)
    result = await trio.run_sync_in_worker_thread(q_s2c.get)
    print("in client: result received", result)

async def parent(q_c2s, q_s2c):
    async with trio.open_nursery() as nursery:
        nursery.start_soon(sleep)
        nursery.start_soon(client, q_c2s, q_s2c)
        nursery.start_soon(sleep)

def main():
    q_c2s = Queue()
    q_s2c = Queue()
    p = Process(target=server, args=(q_c2s, q_s2c))
    p.start()
    trio.run(parent, q_c2s, q_s2c)
    p.join()

if __name__ == '__main__':
    main()

一個簡單的 mpi4py 示例......從三重奏的角度來看,這可能是一個糟糕的工作,但它似乎有效。

通信是通過trio.run_sync_in_worker_thread完成的,所以( 如 Nathaniel J. Smith 所寫)(1)沒有取消(並且沒有 control-C 支持)和(2)使用比 trio 任務更多的內存(但是一個 Python 線程並沒有使用那么多記憶)。

但是對於涉及大型 numpy 數組的通信,我會這樣做,因為使用 mpi4py 進行類似緩沖區的對象的通信將非常有效

import sys
from functools import partial

import trio

import numpy as np
from mpi4py import MPI

async def sleep():
    print("enter sleep")
    await trio.sleep(0.2)
    print("end sleep")

def cpu_bounded_task(input_data):
    print("cpu_bounded_task starting")
    result = input_data.copy()
    for i in range(1000000-1):
        result += input_data
    print("cpu_bounded_task finished ")
    return result

if "server" not in sys.argv:
    comm = MPI.COMM_WORLD.Spawn(sys.executable,
                                args=['trio_spawn_comm_mpi.py', 'server'])

    async def client():
        input_data = np.arange(4)
        print("in client: sending the input_data", input_data)
        send = partial(comm.send, dest=0, tag=0)
        await trio.run_sync_in_worker_thread(send, input_data)

        print("in client: recv")
        recv = partial(comm.recv, tag=1)
        result = await trio.run_sync_in_worker_thread(recv)
        print("in client: result received", result)

    async def parent():
        async with trio.open_nursery() as nursery:
            nursery.start_soon(sleep)
            nursery.start_soon(client)
            nursery.start_soon(sleep)

    trio.run(parent)

    print("in client, end")
    comm.barrier()

else:
    comm = MPI.Comm.Get_parent()

    async def main_server():
        # get the data to be processed
        recv = partial(comm.recv, tag=0)
        input_data = await trio.run_sync_in_worker_thread(recv)
        print("in server: input_data received", input_data)
        # a CPU-bounded task
        result = cpu_bounded_task(input_data)
        print("in server: sending back the answer", result)
        send = partial(comm.send, dest=0, tag=1)
        await trio.run_sync_in_worker_thread(send, result)

    trio.run(main_server)
    comm.barrier()

您還可以查看tractor ,它似乎終於發布了第一個 alpha 版本。

它具有使用 TCP 和msgpack內置功能聚焦式 RPC 系統(很像trio )(但我認為他們計划了更多的傳輸)。 您只需直接調用其他進程中的函數,然后以各種不同的方式將結果流/返回。

這是他們的第一個例子:

"""
Run with a process monitor from a terminal using::

    $TERM -e watch -n 0.1  "pstree -a $$" \
        & python examples/parallelism/single_func.py \
        && kill $!

"""
import os

import tractor
import trio


async def burn_cpu():

    pid = os.getpid()

    # burn a core @ ~ 50kHz
    for _ in range(50000):
        await trio.sleep(1/50000/50)

    return os.getpid()


async def main():

    async with tractor.open_nursery() as n:

        portal = await n.run_in_actor(burn_cpu)

        #  burn rubber in the parent too
        await burn_cpu()

        # wait on result from target function
        pid = await portal.result()

    # end of nursery block
    print(f"Collected subproc {pid}")


if __name__ == '__main__':
    trio.run(main)

暫無
暫無

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

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