파이썬에서 for 루프 병렬화


35

Matlab의 parfor와 유사한 도구가 Python에 있습니까? 이 스레드를 찾았 지만 4 살입니다. 아마 여기에 누군가가 더 최근의 경험이 있다고 생각했습니다.

다음은 병렬화하려는 유형의 예입니다.

X = np.random.normal(size=(10, 3))
F = np.zeros((10, ))
for i in range(10):
    F[i] = my_function(X[i,:])

어디 에서 크기를 my_function취하고 스칼라를 반환합니다.ndarray(1,3)

최소한, 나는 parfor와 같이 여러 개의 코어를 동시에 사용하고 싶습니다. 다시 말해, 8 ~ 16 코어의 공유 메모리 시스템을 가정하십시오.



감사합니다, @ doug-lipinski. 인터넷 검색 중에 찾은 다른 예제와 마찬가지로 이러한 예제는 반복 색인을 기반으로 약간의 계산을합니다. 그리고 그들은 항상 코드가 "놀랍게 쉽다"고 주장합니다. 내 예제는 for 루프 외부의 배열을 정의합니다 (메모리 할당). 다른 방법으로도 괜찮습니다. 그것이 내가 Matlab에서하는 방법입니다. 이러한 예제를 뒷받침하는 까다로운 부분은 주어진 배열의 일부를 루프 내부의 함수에 가져 오는 것입니다.
폴 G. 콘스탄틴

답변:


19

Joblib 은 원하는 것을 수행합니다. 기본 사용법 패턴은 다음과 같습니다.

from joblib import Parallel, delayed

def myfun(arg):
     do_stuff
     return result

results = Parallel(n_jobs=-1, verbose=verbosity_level, backend="threading")(
             map(delayed(myfun), arg_instances))

여기서 병렬로 계산 arg_instances되는 값 목록입니다 myfun. 주요 제한 사항은 myfun최상위 기능이어야한다는 것입니다. backend매개 변수가 될 수 있습니다 "threading"또는 "multiprocessing".

추가 공통 매개 변수를 병렬화 된 함수에 전달할 수 있습니다. 본문은 myfun초기화 된 전역 변수 (자식에 사용 가능한 값)를 나타낼 수도 있습니다.

Args와 결과는 스레딩 백엔드에서 거의 모든 것이 될 수 있지만 결과는 멀티 프로세싱 백엔드에서 직렬화 할 수 있어야합니다.


Dask 도 비슷한 기능을 제공합니다. 코어가 아닌 데이터로 작업하거나 더 복잡한 계산을 병렬화하려는 경우에 바람직 할 수 있습니다.


멀티 프로세싱을 포함하여 배터리를 사용하기 위해 0 값이 추가 된 것을 볼 수 있습니다. joblib이 후드 아래에서 사용하고 있다고 생각했습니다.
Xavier Combelle

1
그것은 JOBLIB 마법 아니라고 언급되어야의 threading로부터 백엔드 겪고있다 GIL 병목 현상multiprocessing백엔드 인해 모든 매개 변수와 반환 값의 직렬화에 큰 오버 헤드를 제공합니다. 파이썬에서 병렬 처리에 대한 저수준 세부 사항은 이 답변 을 참조하십시오 .
Jakub Klinkovský

joblib가 for-loop보다 빠른 함수 복잡성과 반복 횟수의 조합을 찾을 수 없습니다. 나에게 n_jobs = 1이면 속도가 같고 다른 모든 경우에는 훨씬 느리다
Aleksejs Fomins

@AleksejsFomins 스레드 기반 병렬 처리는 GIL을 해제하지는 않지만 특히 데이터 과학 또는 수치 라이브러리와 같은 많은 수의 코드에는 도움이되지 않습니다. 그렇지 않으면 mutiprocessing이 필요합니다. Jobli는 두 가지를 모두 지원합니다. 멀티 프로세싱 모듈에도 이제 map직접 사용할 수있는 병렬 기능 이 있습니다. 또한 mkl 컴파일 된 numpy를 사용하면 아무것도하지 않고 벡터화 된 작업을 자동으로 병렬화합니다. Ananconda의 numpy는 기본적으로 mkl 활성화되어 있습니다. 그러나 보편적 인 해결책은 없습니다. Joblib는 매우 소란스럽고 2015 년에는 더 적은 수의 otions가있었습니다.
Daniel Mahler

조언 해 주셔서 감사합니다. 멀티 프로세싱을 시도하고 심지어 몇 개의 게시물을 작성하는 것을 기억합니다. 아마 또 다른 모습을
보여줘야합니다

9

당신이 찾고있는 것은 Numba 입니다 .for 루프를 자동 병렬화 할 수 있습니다. 그들의 문서에서

from numba import jit, prange

@jit
def parallel_sum(A):
    sum = 0.0
    for i in prange(A.shape[0]):
        sum += A[i]

    return sum

8

my_function선택에 특별한 것을 가정하지 않으면 multiprocessing.Pool().map()그러한 간단한 루프를 병렬화 할 때 좋은 추측입니다. joblib, dask, mpi계산 또는 numba쓸모없는 종속성을 같은 사용하는 경우에 어떤 이점을 가져오고 추가하지 보이는 다른 답변에서 제안처럼 (요약하는 그들은 과잉이다). 코드의 GIL 상호 작용에 친밀해야하거나 코드가 주로 입력 / 출력을 수행해야하기 때문에 다른 답변에서 제안 된 스레딩을 사용하는 것이 좋은 해결책은 아닙니다.

그것은 numba순차 순수 파이썬 코드의 속도를 높이는 것이 좋을지도 모르지만 이것이 문제의 범위를 벗어난 것으로 생각합니다.

import multiprocessing
import numpy as np

if __name__ == "__main__":
   #the previous line is necessary under windows to not execute 
   # main module on each child under windows

   X = np.random.normal(size=(10, 3))
   F = np.zeros((10, ))

   pool = multiprocessing.Pool(processes=16)
   # if number of processes is not specified, it uses the number of core
   F[:] = pool.map(my_function, (X[i,:] for i in range(10)) )

그러나 몇 가지주의 사항이 있지만 대부분의 응용 프로그램에는 영향을 미치지 않습니다.

  • 창 아래에는 포크 지원이 없으므로 각 하위를 시작할 때 메인 모듈이있는 인터프리터가 시작되므로 오버 헤드가있을 수 있습니다 (광고의 이유는 if __name__ == "__main__"
  • my_function의 인수와 결과는 산세 및 비산 세이며, 너무 큰 오버 헤드 일 수 있습니다 . https://stackoverflow.com/a/37072511/128629 를 줄이기 위해이 답변을 참조 하십시오 . 또한 선택 불가능한 객체를 사용할 수 없게 만듭니다.
  • my_function상태는 프로세스간에 공유되지 않기 때문에 전역 변수와의 통신과 같은 공유 상태에 의존해서는 안됩니다. 순수한 함수 (수학적 의미의 함수)는 상태를 공유하지 않는 함수의 예입니다.

6

parfor에 대한 나의 인상은 MATLAB이 구현 세부 사항을 캡슐화한다는 것이므로 공유 메모리 병렬 처리 (원하는 것)와 분산 메모리 병렬 처리 ( MATLAB 분산 컴퓨팅 서버를 실행하는 경우)를 모두 사용할 수 있습니다 .

공유 메모리 병렬 처리를 원하고 일종의 작업 병렬 루프를 실행하는 경우 다중 처리 표준 라이브러리 패키지 는 Doug의 게시물에서 언급 한 것처럼 joblib 과 같은 멋진 프론트 엔드와 함께 원하는 것일 수 있습니다 . 표준 라이브러리는 사라지지 않으며 유지 관리되므로 위험이 적습니다.

Parallel PythonIPython의 병렬 기능 과 같은 다른 옵션도 있습니다 . Parallel Python을 간략히 살펴보면 라이브러리가 분산 된 사례에 대한 세부 정보를 캡슐화한다는 점에서 parfor의 정신에 더 가깝다고 생각하지만 그렇게하는 데 드는 비용은 생태계를 채택해야한다는 것입니다. IPython 사용 비용은 비슷합니다. IPython 방식으로 일을해야합니다. 가치가 있거나 없을 수도 있습니다.

분산 메모리에 관심이 있다면 mpi4py를 권장 합니다 . Lisandro Dalcin은 훌륭한 작업을 수행하며 mpi4py는 PETSc Python 래퍼에 사용되므로 곧 사라질 것이라고 생각하지 않습니다. 멀티 프로세싱과 마찬가지로 parfor보다 병렬 처리에 대한 낮은 수준의 인터페이스이지만 한동안 지속될 수 있습니다.


감사합니다, @Geoff. 이 라이브러리를 사용한 경험이 있습니까? 어쩌면 공유 메모리 머신 / 멀티 코어 프로세서에서 mpi4py를 사용해 볼 것입니다.
Paul G. Constantine

@PaulGConstantine mpi4py를 성공적으로 사용했습니다. MPI에 익숙하다면 고통스럽지 않습니다. 멀티 프로세싱을 사용하지는 않았지만 동료들에게 추천했는데, 동료들에게 잘 작동한다고했습니다. IPython도 사용했지만 병렬 처리 기능은 사용하지 않았으므로 얼마나 잘 작동하는지 말할 수 없습니다.
제프 옥스 베리

1
Aron은 Supercomputing에서 PyHPC 코스를 위해 준비한 멋진 mpi4py 튜토리얼을 가지고 있습니다 : github.com/pyHPC/pyhpc-tutorial
Matt Knepley

4

병렬 "일반"파이썬 함수에서 실행하는 데 사용할 수있는 "블랙 박스"도구를 찾기 전에 my_function()손으로 병렬화 하는 방법을 분석하는 것이 좋습니다 .

먼저, 실행 시간 my_function(v)을 파이썬 for루프 오버 헤드 와 비교하십시오 . [C] Python for루프는 매우 느리기 때문에 시간을 my_function()무시할 수 있습니다.

>>> timeit.timeit('pass', number=1000000)
0.01692986488342285
>>> timeit.timeit('for i in range(10): pass', number=1000000)
0.47521495819091797
>>> timeit.timeit('for i in xrange(10): pass', number=1000000)
0.42337894439697266

my_function(v)루프를 필요로하지 않는 간단한 벡터 구현이 있는지 두 번째 확인하십시오 .F[:] = my_vector_function(X)

(이 두 가지 첫 번째 요점은 매우 사소한 것입니다. 완전성을 위해 여기에 언급했다면 용서하십시오.)

적어도 CPython 구현에서 세 번째이자 가장 중요한 점 my_function은 대부분의 시간을 전역 인터프리터 잠금 또는 GIL 내부 또는 외부 에서 보내는 지 확인하는 것 입니다. GIL 외부에서 시간이 소요되면 표준 라이브러리 모듈을 사용해야합니다. ( 여기 예). BTW, GIL을 릴리스하기 위해 C 확장 으로 작성 하는 것을 생각할 수 있습니다.threadingmy_function()

마지막으로 my_function()GIL을 해제하지 않으면 multiprocessing모듈을 사용할 수 있습니다 .

참조 : 동시 실행에 대한 Python 문서병렬 처리에 대한 numpy / scipy 소개 .


2

줄리아를 시험해 볼 수 있습니다. 파이썬과 매우 가까우며 많은 MATLAB 구문이 있습니다. 번역은 다음과 같습니다.

F = @parallel (vcat) for i in 1:10
    my_function(randn(3))
end

이것은 난수도 병렬로 만들고 축소하는 동안 결과를 연결합니다. 멀티 프로세싱을 사용하므로 (사용 addprocs(N)하기 전에 프로세스를 추가 해야하며 , 이 블로그 게시물에 표시된대로 HPC의 여러 노드에서도 작동합니다 .)

pmap대신 사용할 수도 있습니다 .

F = pmap((i)->my_function(randn(3)),1:10)

스레드 병렬 처리를 원할 경우 Threads.@threads알고리즘을 스레드로부터 안전하게 만들 수 있지만 사용할 수 있습니다 . Julia를 열기 전에 환경 변수 JULIA_NUM_THREADS를 설정하면 다음과 같습니다.

Ftmp = [Float64[] for i in Threads.nthreads()]
Threads.@threads for i in 1:10
    push!(Ftmp[Threads.threadid()],my_function(randn(3)))
end
F = vcat(Ftmp...)

여기에서는 각 스레드마다 별도의 배열을 만들어 배열에 추가 할 때 충돌하지 않고 나중에 배열을 연결합니다. 스레딩은 매우 새롭기 때문에 지금 바로 스레드를 사용하고 있지만 스레드 축소 및 맵이 멀티 프로세싱과 마찬가지로 추가 될 것이라고 확신합니다.


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