파이썬에서 병렬 프로그래밍을 수행하는 방법?


141

C ++의 경우 OpenMP를 사용하여 병렬 프로그래밍을 수행 할 수 있습니다. 그러나 OpenMP는 Python에서 작동하지 않습니다. 파이썬 프로그램의 일부를 병렬화하려면 어떻게해야합니까?

코드의 구조는 다음과 같이 간주 될 수 있습니다.

solve1(A)
solve2(B)

어디에 solve1그리고 solve2두 개의 독립적 인 기능입니다. 실행 시간을 줄이기 위해 이러한 종류의 코드를 순차적이 아닌 병렬로 실행하는 방법은 무엇입니까? 누군가 나를 도울 수 있기를 바랍니다. 미리 감사드립니다. 코드는 다음과 같습니다

def solve(Q, G, n):
    i = 0
    tol = 10 ** -4

    while i < 1000:
        inneropt, partition, x = setinner(Q, G, n)
        outeropt = setouter(Q, G, n)

        if (outeropt - inneropt) / (1 + abs(outeropt) + abs(inneropt)) < tol:
            break

        node1 = partition[0]
        node2 = partition[1]

        G = updateGraph(G, node1, node2)

        if i == 999:
            print "Maximum iteration reaches"
    print inneropt

setinner와 setouter는 두 개의 독립적 인 기능입니다. 그것이 내가 평행하게하고 싶은 곳입니다 ...


31
멀티 프로세싱을 살펴보십시오 . 참고 : Python의 스레드는 CPU 바인딩 작업에는 적합하지 않으며 I / O 바인딩에만 적합합니다.
9000

4
CPU 대 I / O 종속 작업을 언급하는 @ 9000 +100 인터넷.
Hyperboreus

@ 9000 실제로 스레드는 내가 아는 한 CPU 바운드 작업에 전혀 적합하지 않습니다! 프로세스는 실제 CPU 바운드 작업을 수행 할 때 사용하는 방법입니다.
Omar Al-Ithawi

6
@ OmarIthawi : 왜 CPU 코어가 많은 경우 스레드가 정상적으로 작동합니까 (지금과 같이). 그런 다음 프로세스를 병렬로 모든 코어를로드하는 여러 스레드 실행 하고 (명시 적 공유 메모리 영역 또는 프로세스 간 메시징을 필요없이입니다) 암시 적으로 그들 사이의 공통 데이터를 공유합니다.
9000

1
@ user2134774 : 글쎄, 내 두 번째 의견은 거의 이해가되지 않습니다. 아마도 GIL을 릴리스하는 유일한 C 확장은 그 혜택을 누릴 수 있습니다. 예를 들어 NumPy와 Pandas의 일부가 그렇게합니다. 다른 경우에는 잘못되었습니다 (그러나 지금 편집 할 수는 없습니다).
9000

답변:


162

다중 처리 모듈을 사용할 수 있습니다 . 이 경우 처리 풀을 사용할 수 있습니다.

from multiprocessing import Pool
pool = Pool()
result1 = pool.apply_async(solve1, [A])    # evaluate "solve1(A)" asynchronously
result2 = pool.apply_async(solve2, [B])    # evaluate "solve2(B)" asynchronously
answer1 = result1.get(timeout=10)
answer2 = result2.get(timeout=10)

이렇게하면 일반적인 작업을 수행 할 수있는 프로세스가 생성됩니다. 통과하지 않았으므로 processes머신의 각 CPU 코어마다 하나의 프로세스가 생성됩니다. 각 CPU 코어는 하나의 프로세스를 동시에 실행할 수 있습니다.

목록을 단일 함수에 매핑하려면 다음을 수행하십시오.

args = [A, B]
results = pool.map(solve1, args)

GIL 은 파이썬 객체에 대한 작업을 잠그기 때문에 스레드를 사용하지 마십시오 .


1
수행 pool.map도 인수로 사전을 받아? 아니면 간단한 목록 만?
Bndr

내가 생각하는 목록 만 그러나 dict.items ()를 전달하면 주요 튜플 목록이됩니다.
Matt Williamson

불행히도 이것은 '해시 불가능한 유형 :'목록 '오류로 끝납니다
Bndr

내 마지막 주석 외에도`dict.items ()`work. 변수 통찰력 처리 프로세스를 변경해야했기 때문에 오류가 발생합니다. 불행히도 오류 메시지는 그다지 도움이되지 못했습니다 ... 힌트 : 힌트 주셔서 감사합니다. :-)
Bndr

2
타임 아웃이란 무엇입니까?
gamma

26

이것은 Ray를 사용 하여 매우 우아하게 수행 할 수 있습니다 .

예제를 병렬화하려면 @ray.remote데코레이터로 함수를 정의한 다음로 호출해야합니다 .remote.

import ray

ray.init()

# Define the functions.

@ray.remote
def solve1(a):
    return 1

@ray.remote
def solve2(b):
    return 2

# Start two tasks in the background.
x_id = solve1.remote(0)
y_id = solve2.remote(1)

# Block until the tasks are done and get the results.
x, y = ray.get([x_id, y_id])

멀티 프로세싱 모듈 에 비해 여러 가지 장점이 있습니다.

  1. 멀티 코어 머신과 머신 클러스터에서 동일한 코드가 실행됩니다.
  2. 프로세스는 공유 메모리 및 제로 카피 직렬화를 통해 데이터를 효율적으로 공유합니다 .
  3. 오류 메시지가 잘 전달됩니다.
  4. 이 함수 호출은 예를 들어

    @ray.remote
    def f(x):
        return x + 1
    
    x_id = f.remote(1)
    y_id = f.remote(x_id)
    z_id = f.remote(y_id)
    ray.get(z_id)  # returns 4
  5. 함수를 원격으로 호출하는 것 외에도 클래스를 원격으로 액터 로 인스턴스화 할 수 있습니다 .

참고 레이 내가 개발할 수 있도록했습니다 프레임 워크입니다.


파이썬에서 패키지를 설치하려고 : 나는 "일치 유통 선에 대한 발견 만족 (버전) 요구 사항 레이가있는 버전을 찾을 수 없습니다"라는 오류가 계속
alwaysaskingquestions

2
일반적으로 이러한 종류의 오류는 업그레이드해야 함을 의미합니다 pip. 시도해 볼 것을 제안 pip install --upgrade pip합니다. 당신이 사용해야 할 경우 sudo모두에서 그것은 버전있을 가능성이 pip설치할 사용하고 있는지를 ray업그레이드하기 동일한 아니다. 로 확인할 수 있습니다 pip --version. 또한 Windows는 현재 지원되지 않으므로 Windows를 사용하는 경우 문제 일 수 있습니다.
Robert Nishihara

1
이것은 주로 여러 시스템에 동시 작업을 분배하기위한 것입니다.
Matt Williamson

2
실제로 단일 머신 케이스와 클러스터 설정 모두에 최적화되어 있습니다. 많은 설계 결정 (예 : 공유 메모리, 무 복사 직렬화)은 단일 시스템을 잘 지원하는 것을 목표로합니다.
Robert Nishihara

2
문서가 더 지적하면 좋을 것입니다. 나는 실제로 단일 머신 케이스를위한 것이 아니라는 문서를 읽은 감각을 얻었습니다.
썰매


4

다른 사람들이 말했듯이 해결책은 여러 프로세스를 사용하는 것입니다. 그러나 어떤 프레임 워크가 더 적합한지는 많은 요소에 달려 있습니다. 이미 언급 한 것 외에도 charm4pympi4py ( charm4py 의 개발자입니다)도 있습니다.

작업자 풀 추상화를 사용하는 것보다 위의 예를 구현하는 것이 더 효율적인 방법입니다. 메인 루프는 G1000 개의 반복마다 작업자 에게 동일한 매개 변수 (전체 그래프 포함)를 반복해서 보냅니다 . 최소한 한 명의 작업자가 다른 프로세스에 상주하므로 인수를 다른 프로세스로 복사하여 보내야합니다. 객체의 크기에 따라 비용이 많이들 수 있습니다. 대신, 근로자가 상태를 저장하고 업데이트 된 정보를 보내도록하는 것이 좋습니다.

예를 들어, charm4py에서 다음과 같이 할 수 있습니다 :

class Worker(Chare):

    def __init__(self, Q, G, n):
        self.G = G
        ...

    def setinner(self, node1, node2):
        self.updateGraph(node1, node2)
        ...


def solve(Q, G, n):
    # create 2 workers, each on a different process, passing the initial state
    worker_a = Chare(Worker, onPE=0, args=[Q, G, n])
    worker_b = Chare(Worker, onPE=1, args=[Q, G, n])
    while i < 1000:
        result_a = worker_a.setinner(node1, node2, ret=True)  # execute setinner on worker A
        result_b = worker_b.setouter(node1, node2, ret=True)  # execute setouter on worker B

        inneropt, partition, x = result_a.get()  # wait for result from worker A
        outeropt = result_b.get()  # wait for result from worker B
        ...

이 예에서는 실제로 한 명의 작업자 만 필요합니다. 메인 루프는 함수 중 하나를 실행하고 작업자가 다른 함수를 실행하도록 할 수 있습니다. 그러나 내 코드는 몇 가지 사항을 설명하는 데 도움이됩니다.

  1. 작업자 A는 프로세스 0에서 실행됩니다 (메인 루프와 동일). result_a.get()작업자 A는 결과를 기다리면서 차단 되지만 작업자 A는 동일한 프로세스에서 계산을 수행합니다.
  2. 인수는 동일한 프로세스에 있기 때문에 작업자 A를 참조하여 자동으로 전달됩니다 (복사는 포함되지 않음).

2

경우에 따라 Numba 를 사용하여 루프를 자동으로 병렬화하는 것이 가능 하지만 작은 Python 하위 집합에서만 작동합니다.

from numba import njit, prange

@njit(parallel=True)
def prange_test(A):
    s = 0
    # Without "parallel=True" in the jit-decorator
    # the prange statement is equivalent to range
    for i in prange(A.shape[0]):
        s += A[i]
    return s

불행히도 Numba는 Numpy 배열에서만 작동하지만 다른 Python 객체에서는 작동하지 않는 것 같습니다. 이론적으로 파이썬을 C ++컴파일 한 다음 Intel C ++ 컴파일러를 사용하여 자동으로 병렬 처리 할 수는 있지만 아직 시도하지는 않았습니다.


2

joblib라이브러리를 사용 하여 병렬 계산 및 다중 처리를 수행 할 수 있습니다 .

from joblib import Parallel, delayed

foo병렬로 실행하려는 함수 를 작성 하고 다음 코드 구현 병렬 처리를 기반으로합니다.

output = Parallel(n_jobs=num_cores)(delayed(foo)(i) for i in input)

다음과 같이 도서관 num_cores에서 얻을 수있는 곳 multiprocessing:

import multiprocessing

num_cores = multiprocessing.cpu_count()

입력 인수가 둘 이상인 함수가 있고 목록으로 인수 중 하나를 반복하려는 경우 라이브러리 의 partial함수를 functools다음과 같이 사용할 수 있습니다 .

from joblib import Parallel, delayed
import multiprocessing
from functools import partial
def foo(arg1, arg2, arg3, arg4):
    '''
    body of the function
    '''
    return output
input = [11,32,44,55,23,0,100,...] # arbitrary list
num_cores = multiprocessing.cpu_count()
foo_ = partial(foo, arg2=arg2, arg3=arg3, arg4=arg4)
# arg1 is being fetched from input list
output = Parallel(n_jobs=num_cores)(delayed(foo_)(i) for i in input)

python 및 R 멀티 프로세싱에 대한 자세한 설명은 여기 에서 몇 가지 예를 참조하십시오 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.