Python에서 하위 프로세스, 다중 처리 및 스레드 중에서 결정합니까?


110

Python 프로그램이 실행되는 컴퓨터에서 여러 프로세서를 사용할 수 있도록 Python 프로그램을 병렬화하고 싶습니다. 내 병렬화는 프로그램의 모든 병렬 "스레드"가 독립적이고 출력을 별도의 파일에 기록한다는 점에서 매우 간단합니다. 정보를 교환하기 위해 스레드가 필요하지는 않지만 파이프 라인의 일부 단계가 출력에 따라 다르기 때문에 스레드가 언제 완료되는지 아는 것이 중요합니다.

Mac, Linux 및 Windows의 모든 Python 버전에서 실행되기를 원한다는 점에서 이식성이 중요합니다. 이러한 제약을 감안할 때이를 구현하는 데 가장 적합한 Python 모듈은 무엇입니까? 관련 기능을 모두 제공하는 것으로 보이는 스레드, 하위 프로세스 및 다중 처리 중에서 결정하려고합니다.

이것에 대한 어떤 생각? 이식 가능한 가장 간단한 솔루션을 원합니다.


관련 : stackoverflow.com/questions/1743293/… (쓰레드가 순수한 Python 코드의 시작이 아닌 이유를 보려면 내 답변을 읽으십시오)

1
"모든 파이썬 버전"은 너무 모호합니다. 파이썬 2.3? 1.x? 3.x? 만족하기에는 불가능한 조건입니다.
detly

답변:


64

multiprocessing스위스 군용 칼 유형의 모듈입니다. 원격 계산을 수행 할 수도 있으므로 스레드보다 더 일반적입니다. 따라서 이것은 내가 사용하도록 제안하는 모듈입니다.

subprocess모듈을 사용하면 여러 프로세스를 시작할 수도 있지만 새로운 다중 처리 모듈보다 사용하기가 덜 편리하다는 것을 알았습니다.

스레드는 미묘한 것으로 악명이 높으며 CPython을 사용하면 종종 하나의 코어로 제한됩니다 (주석 중 하나에서 언급했듯이 GIL (Global Interpreter Lock)은 Python 코드에서 호출 된 C 코드로 해제 될 수 있음). .

나는 당신이 인용하는 세 가지 모듈의 대부분의 기능이 플랫폼 독립적 인 방식으로 사용될 수 있다고 믿습니다. 이식성 측면에서는 multiprocessingPython 2.6 이후 표준으로 만 제공됩니다 (일부 이전 버전의 Python 용 버전이 존재 함). 그러나 그것은 훌륭한 모듈입니다!


1
할당을 위해 방금 "multiprocessing"모듈과 pool.map () 메서드를 사용했습니다. 케이크 조각!
kmonsoor 2014

셀러리 같은 것도 고려 중입니까? 왜 그렇지 않습니까?
user3245268

내가 말할 수있는 한 Celery가 더 관련되어 있다고 말할 수 있지만 (메시지 브로커를 설치해야 함) 당면한 문제에 따라 아마도 고려해야 할 옵션입니다.
Eric O Lebigot 2019

186

나에게 이것은 실제로 매우 간단합니다.

서브 프로세스 옵션 :

subprocess입니다 다른 실행 파일을 실행하는 것이 주위에 기본적으로 래퍼 --- os.fork()os.execve()옵션 배관에 대한 일부 지원 (에와 하위 프로세스에서 파이프를 설정. 분명히 그럴 수있어 다른 프로세스 간 통신 (IPC)와 같은 소켓 같은 메커니즘, 또는 POSIX 또는 SysV 공유 메모리하지만 호출하는 프로그램에서 지원하는 인터페이스와 IPC 채널로 제한됩니다.

일반적으로 누군가는 subprocess동기식으로 --- 단순히 외부 유틸리티를 호출하고 출력을 다시 읽거나 완료를 기다립니다 (임시 파일에서 결과를 읽거나 데이터베이스에 게시 한 후).

그러나 수백 개의 하위 프로세스를 생성하고 폴링 할 수 있습니다. 내가 개인적으로 가장 좋아하는 유틸리티 클래스 는 정확히 그렇게합니다. 모듈 의 가장 큰 단점subprocessI / O 지원이 일반적으로 차단된다는 것입니다. 일부 향후 버전의 Python 3.x 및 대체 asyncproc (모든 종류의 문서 나 README가 아닌 다운로드로 연결되는 경고) 에서이를 수정 하는 초안 PEP-3145 가 있습니다. 또한 PIPE 파일 디스크립터를 직접 가져 오고 조작 하는 것이 비교적 쉽다는 것을 발견했습니다. 비록 이것이 비 UNIX 플랫폼에 이식 가능한지 모르겠지만.fcntlPopen

(업데이트 : 2019 년 8 월 7 일 : ayncio 하위 프로세스에 대한 Python 3 지원 : asyncio 하위 프로세스 )

subprocess 이벤트 핸들링 지원이 거의 없습니다 ... 모듈과 평범한 구식 UNIX / Linux 신호를 사용할 수 있지만signal --- 프로세스를 부드럽게 종료합니다.

멀티 프로세싱 옵션 :

multiprocessing이 프로세스 제품군 간의보다 유연한 통신을 지원 하는 기존 (Python) 코드 내에서 함수를 실행 하기위한 것입니다. 특히 가능한 multiprocessing경우 모듈의 Queue객체를 중심으로 IPC 를 구축하는 것이 가장 좋지만 Event객체 및 다양한 기타 기능을 사용할 수도 있습니다 (일부는 mmap지원이 충분한 플랫폼에서 지원을 기반으로 구축 된 것 같습니다 ).

Python의 multiprocessing모듈은 CPython이 GIL (Global Interpreter Lock)에도 불구하고 여러 CPU / 코어 사이에서 처리를 확장 할 수 있도록 허용하면서 매우 유사한 인터페이스 및 기능을 제공하기위한 것 threading입니다. OS 커널 개발자가 수행 한 모든 세분화 된 SMP 잠금 및 일관성 노력을 활용합니다.

스레딩 옵션 :

threadingI / O 바운드 (여러 CPU 코어에 걸쳐 확장 할 필요가 없음)이고 스레드 스위칭 (공유 코어 메모리 사용) 대 프로세스 /의 매우 낮은 대기 시간 및 스위칭 오버 헤드의 이점 이 있는 상당히 좁은 범위의 애플리케이션을위한 것입니다. 컨텍스트 전환. Linux에서 이것은 거의 빈 집합입니다 (Linux 프로세스 전환 시간은 스레드 스위치에 매우 가깝습니다).

threading앓고 파이썬에서 두 가지 주요 단점 .

물론 하나는 특정 구현입니다. --- 대부분 CPython에 영향을 미칩니다. 그것이 GIL입니다. 대부분의 경우 대부분의 CPython 프로그램은 두 개 이상의 CPU (코어) 가용성의 이점을 얻지 못하며 종종 GIL 잠금 경합으로 인해 성능이 저하 됩니다.

구현과 관련이없는 더 큰 문제는 스레드가 동일한 메모리, 신호 처리기, 파일 설명자 및 특정 기타 OS 리소스를 공유한다는 것입니다. 따라서 프로그래머는 개체 잠금, 예외 처리 및 전체 프로세스 (스레드 모음)를 종료, 중단 또는 교착 상태로 만들 수있는 코드의 기타 측면에 대해 매우주의해야합니다.

비교해 보면 multiprocessing모델은 각 프로세스에 자체 메모리, 파일 설명자 등을 제공합니다. 그중 하나에서 충돌 또는 처리되지 않은 예외는 해당 리소스 만 죽이고 자식 또는 형제 프로세스의 사라짐을 강력하게 처리하는 것이 디버깅, 격리보다 훨씬 쉬울 수 있습니다. 스레드에서 유사한 문제를 수정하거나 해결합니다.

  • (참고 : NumPythreading 와 같은 주요 Python 시스템과 함께 사용하면 대부분의 Python 코드보다 GIL 경합으로 인한 피해가 훨씬 적을 수 있습니다. 그 이유는 NumPy의 네이티브 / 바이너리 부분, 예를 들어 안전 할 때 GIL을 해제합니다.)

트위스트 옵션 :

Twisted우아하고 이해하기 매우 어려운 또 다른 대안을 제공 한다는 점도 주목할 가치가 있습니다. 기본적으로 Twisted 팬이 갈퀴와 횃불로 우리 집을 휩쓸 수있을 정도로 지나치게 단순화 할 위험이있는 Twisted는 모든 (단일) 프로세스 내에서 이벤트 중심의 협동 멀티 태스킹을 제공합니다.

이것이 어떻게 가능한지 이해하려면 select()( select () 또는 poll () 또는 유사한 OS 시스템 호출을 중심으로 구축 될 수있는) 의 기능에 대해 읽어야 합니다. 기본적으로 파일 설명자 목록 또는 일부 시간 초과에 대한 활동을 보류하기 위해 OS가 절전 모드를 요청하는 기능에 의해 구동됩니다.

이러한 각 호출에서 깨어나 select()는 것은 이벤트입니다. 일부 수의 소켓 또는 파일 설명자에서 사용 가능한 (읽을 수있는) 입력이 포함되거나 다른 (쓰기 가능한) 설명자 또는 소켓에서 사용 가능한 버퍼링 공간, 일부 예외적 인 조건 (TCP 예를 들어 대역 외 PUSH 패킷) 또는 TIMEOUT.

따라서 Twisted 프로그래밍 모델은 이러한 이벤트를 처리 한 다음 결과 "주"처리기를 반복하여 이벤트를 처리기에 전달할 수 있도록 구축되었습니다.

저는 개인적으로 프로그래밍 모델을 연상시키는 Twisted 라는 이름을 생각 합니다. 문제에 대한 접근 방식은 어떤 의미에서는 내부가 "뒤틀려"있어야하기 때문입니다. 프로그램을 입력 데이터 및 출력 또는 결과에 대한 일련의 작업으로 생각하는 대신 프로그램을 서비스 또는 데몬으로 작성하고 다양한 이벤트에 반응하는 방식을 정의합니다. (사실 Twisted 프로그램의 핵심 "주 루프"는 (보통? 항상?) a reactor())입니다.

Twisted 사용에 대한 주요 과제 는 이벤트 중심 모델에 대한 마음을 비틀고 Twisted 프레임 워크 내에서 협력하도록 작성되지 않은 클래스 라이브러리 또는 툴킷의 사용을 피하는 것입니다. 이것이 Twisted가 SSH 프로토콜 처리, curses 및 자체 하위 프로세스 / Popen 함수를위한 자체 모듈과 처음에는 Python 표준 라이브러리에서 중복되는 것처럼 보이는 다른 많은 모듈 및 프로토콜 핸들러를 제공하는 이유입니다.

Twisted를 사용하지 않더라도 개념적 수준에서 이해하는 것이 유용하다고 생각합니다. 스레딩, 다중 처리 및 하위 프로세스 처리 및 수행하는 분산 처리의 성능, 경합 및 이벤트 처리에 대한 통찰력을 제공 할 수 있습니다.

( 참고 : Python 3.x의 최신 버전에는 async def , @ async.coroutine 데코레이터 및 await 키워드 와 같은 asyncio (비동기 I / O) 기능이 포함되어 있으며 향후 지원 에서 산출됩니다 .이 모든 기능은 다음과 거의 유사합니다. 트위스트 과정 (협동 멀티 태스킹) 관점)에서. (Python 3에 대한 Twisted 지원의 현재 상태는 https://twistedmatrix.com/documents/current/core/howto/python3.html )

배포 옵션 :

아직 묻지 않았지만 고려할 가치가있는 또 다른 처리 영역은 분산 처리입니다. 분산 처리 및 병렬 계산을위한 많은 Python 도구와 프레임 워크가 있습니다. 개인적으로 가장 사용하기 쉬운 것은 그 공간에있는 것으로 가장 적게 간주되는 것입니다.

Redis를 중심으로 분산 처리를 구축하는 것은 거의 간단합니다 . 전체 키 저장소는 작업 단위 및 결과를 저장하는 데 사용될 수 있으며, Redis LIST는 Queue()유사한 객체 로 사용될 수 있으며 , PUB / SUB 지원은 Event유사한 처리에 사용될 수 있습니다 . 느슨한 Redis 인스턴스 클러스터에 복제 된 키를 해시하고 값을 사용하여 토폴로지 및 해시 토큰 매핑을 저장하여 작업자를 조정하기 위해 단일 인스턴스의 용량 이상으로 확장하기위한 일관된 해싱 및 장애 조치를 제공 할 수 있습니다. 그리고 그들 사이에서 데이터 (pickled, JSON, BSON 또는 YAML)를 마샬링합니다.

물론 Redis를 중심으로 더 크고 정교한 솔루션을 구축하기 시작하면 Celery , Apache SparkHadoop , Zookeeper , etcd , Cassandra 등을 사용하여 이미 해결 된 많은 기능을 다시 구현 하게됩니다. 이들 모두는 서비스에 대한 Python 액세스를위한 모듈을 가지고 있습니다.

[업데이트 : 분산 시스템 전반에 걸쳐 계산 집약적 인 Python을 고려하는 경우 고려해야 할 몇 가지 리소스 : IPython ParallelPySpark . 이들은 범용 분산 컴퓨팅 시스템이지만 특히 액세스 가능하고 인기있는 하위 시스템 데이터 과학 및 분석입니다].

결론

단일 스레드에서 하위 프로세스에 대한 간단한 동기 호출, 폴링 된 하위 프로세스 풀, 스레드 및 다중 처리, 이벤트 기반 공동 작업 멀티 태스킹, 분산 처리에 이르기까지 Python을위한 처리 대안의 범위가 있습니다.


1
하지만 클래스 / OOP와 함께 다중 처리를 사용하는 것은 어렵습니다.
Tjorriemorrie 2015

2
@Tjorriemorrie : 다른 프로세스에있을 수있는 개체의 인스턴스에 메서드 호출을 전달하는 것이 어렵다는 것을 의미합니다. 나는 이것이 스레드와 동일한 문제이지만 더 쉽게 볼 수 있다고 제안합니다 (깨지기 쉽고 경쟁 조건이 모호하기보다는). 권장되는 접근 방식은 단일 스레드, 다중 스레드 및 여러 프로세스에서 작동하는 Queue 개체를 통해 이러한 모든 디스패치가 발생하도록하는 것입니다. (노드 클러스터에서도 일부 Redis 또는 Celery Queue 구현 포함)
Jim Dennis

2
이것은 정말 좋은 대답입니다. Python3 문서의 동시성 소개에 있었으면합니다.
root-11

1
@ root-11 문서 관리자에게 제안해도 좋습니다. 여기에 무료로 게시했습니다. 당신과 그들은 그것을 전체 또는 부분적으로 사용할 수 있습니다.
Jim Dennis

"저에게 이것은 실제로 매우 간단합니다 :"그것을 사랑하십시오. 감사합니다
jerome

5

비슷한 경우에 나는 별도의 프로세스와 네트워크 소켓을 통해 필요한 약간의 통신을 선택했습니다. 이식성이 뛰어나고 파이썬을 사용하는 것은 매우 간단하지만 아마도 더 간단하지는 않을 것입니다 (제 경우에는 C ++로 작성된 다른 프로세스와의 통신이라는 또 다른 제약도있었습니다).

귀하의 경우에는 적어도 CPython을 사용할 때 파이썬 스레드가 실제 스레드가 아니기 때문에 다중 프로세스를 사용할 것입니다. 글쎄, 그것들은 네이티브 시스템 스레드이지만 Python에서 호출 된 C 모듈은 GIL을 해제하거나 차단 코드를 호출 할 때 다른 스레드가 실행되도록 허용하지 않을 수 있습니다.


4

CPython에서 여러 프로세서를 사용하려면 모듈 선택하면 multiprocessing됩니다. CPython 은 다른 CPU의 스레드가 병렬로 작동하는 것을 방지하는 내부 ( GIL ) 에 대한 잠금을 유지합니다 . multiprocessing모듈 (같은 새로운 프로세스를 생성한다 subprocess)와 그 사이의 통신을 관리합니다.


5
그것은 사실이 아닙니다. AFAIK는 C API를 사용하여 GIL을 릴리스 할 수 있으며, 이러한 제한을받지 않는 IronPython 또는 Jython과 같은 Python의 다른 구현이 있습니다. 그래도 반대 투표하지 않았습니다.
Bastien Léonard

1

당신의 일을 수행하기 위해 유닉스를 꺼내십시오.

iterpipe 를 사용 하여 하위 프로세스를 래핑 한 다음 :

Ted Ziuba 사이트에서

INPUTS_FROM_YOU | xargs -n1 -0 -P NUM ./process #NUM 병렬 프로세스

또는

Gnu Parallel 은 또한

멀티 코어 작업을 위해 백룸 소년을 보내는 동안 GIL과 어울립니다.


6
"Mac, Linux 및 Windows의 모든 Python 버전에서 실행되기를 원한다는 점에서 이식성이 중요합니다."
detly

이 솔루션으로 작업과 반복적으로 상호 작용할 수 있습니까? 다중 처리에서 이것을 할 수 있지만 하위 프로세스에서는 그렇게 생각하지 않습니다.
abalter
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.