Python에서 하위 프로세스, 다중 처리 및 스레드 중에서 결정합니까?
Python 프로그램이 실행되는 컴퓨터에서 여러 프로세서를 사용할 수 있도록 Python 프로그램을 병렬화하고 싶습니다. 내 병렬화는 프로그램의 모든 병렬 "스레드"가 독립적이고 출력을 별도의 파일에 기록한다는 점에서 매우 간단합니다. 정보를 교환하기 위해 스레드가 필요하지는 않지만 파이프 라인의 일부 단계가 출력에 따라 다르기 때문에 스레드가 언제 완료되는지 아는 것이 중요합니다.
Mac, Linux 및 Windows의 모든 Python 버전에서 실행되기를 원한다는 점에서 이식성이 중요합니다. 이러한 제약을 감안할 때이를 구현하는 데 가장 적합한 Python 모듈은 무엇입니까? 관련 기능을 모두 제공하는 것으로 보이는 스레드, 하위 프로세스 및 다중 처리 중에서 결정하려고합니다.
이것에 대한 어떤 생각? 이식 가능한 가장 간단한 솔루션을 원합니다.
multiprocessing
스위스 군용 칼 유형의 모듈입니다. 원격 계산을 수행 할 수도 있으므로 스레드보다 더 일반적입니다. 따라서 이것은 내가 사용하도록 제안하는 모듈입니다.
이 subprocess
모듈을 사용하면 여러 프로세스를 시작할 수도 있지만 새로운 다중 처리 모듈보다 사용하기가 덜 편리하다는 것을 알았습니다.
스레드는 미묘한 것으로 악명이 높으며 CPython을 사용하면 종종 하나의 코어로 제한됩니다 (주석 중 하나에서 언급했듯이 GIL (Global Interpreter Lock)은 Python 코드에서 호출 된 C 코드로 해제 될 수 있음). .
나는 당신이 인용하는 세 가지 모듈의 대부분의 기능이 플랫폼 독립적 인 방식으로 사용될 수 있다고 믿습니다. 이식성 측면에서는 multiprocessing
Python 2.6 이후 표준으로 만 제공됩니다 (일부 이전 버전의 Python 용 버전이 존재 함). 그러나 그것은 훌륭한 모듈입니다!
나에게 이것은 실제로 매우 간단합니다.
서브 프로세스 옵션 :
subprocess
입니다 다른 실행 파일을 실행하는 것이 주위에 기본적으로 래퍼 --- os.fork()
및 os.execve()
옵션 배관에 대한 일부 지원 (에와 하위 프로세스에서 파이프를 설정. 분명히 그럴 수있어 다른 프로세스 간 통신 (IPC)와 같은 소켓 같은 메커니즘, 또는 POSIX 또는 SysV 공유 메모리하지만 호출하는 프로그램에서 지원하는 인터페이스와 IPC 채널로 제한됩니다.
일반적으로 누군가는 subprocess
동기식으로 --- 단순히 외부 유틸리티를 호출하고 출력을 다시 읽거나 완료를 기다립니다 (임시 파일에서 결과를 읽거나 데이터베이스에 게시 한 후).
그러나 수백 개의 하위 프로세스를 생성하고 폴링 할 수 있습니다. 내가 개인적으로 가장 좋아하는 유틸리티 클래스 는 정확히 그렇게합니다. 모듈 의 가장 큰 단점 은 subprocess
I / O 지원이 일반적으로 차단된다는 것입니다. 일부 향후 버전의 Python 3.x 및 대체 asyncproc (모든 종류의 문서 나 README가 아닌 다운로드로 바로 연결되는 경고) 에서이를 수정 하는 초안 PEP-3145 가 있습니다. 또한 PIPE 파일 디스크립터를 직접 가져 오고 조작 하는 것이 비교적 쉽다는 것을 발견했습니다. 비록 이것이 비 UNIX 플랫폼에 이식 가능한지 모르겠지만.fcntl
Popen
(업데이트 : 2019 년 8 월 7 일 : ayncio 하위 프로세스에 대한 Python 3 지원 : https://docs.python.org/3/library/asyncio-subprocess.html )
subprocess
이벤트 핸들링 지원이 거의 없습니다 ... 모듈과 평범한 구식 UNIX / Linux 신호를 사용할 수 있지만signal
--- 프로세스를 부드럽게 종료합니다.
멀티 프로세싱 옵션 :
multiprocessing
이 프로세스 제품군 간의보다 유연한 통신을 지원 하는 기존 (Python) 코드 내에서 함수를 실행 하기위한 것입니다. 특히 가능한 multiprocessing
경우 모듈의 Queue
객체를 중심으로 IPC 를 구축하는 것이 가장 좋지만 Event
객체 및 다양한 기타 기능을 사용할 수도 있습니다 (일부는 mmap
지원이 충분한 플랫폼에서 지원을 기반으로 구축 된 것 같습니다 ).
Python의 multiprocessing
모듈은 CPython이 GIL (Global Interpreter Lock)에도 불구하고 여러 CPU / 코어 사이에서 처리를 확장 할 수 있도록 허용하면서 매우 유사한 인터페이스 및 기능을 제공하기위한 것 threading
입니다. OS 커널 개발자가 수행 한 모든 세분화 된 SMP 잠금 및 일관성 노력을 활용합니다.
스레딩 옵션 :
threading
I / O 바운드 (여러 CPU 코어에 걸쳐 확장 할 필요가 없음)이고 스레드 스위칭 (공유 코어 메모리 사용) 대 프로세스 /의 매우 낮은 대기 시간 및 스위칭 오버 헤드의 이점 이 있는 상당히 좁은 범위의 애플리케이션을위한 것입니다. 컨텍스트 전환. Linux에서 이것은 거의 빈 집합입니다 (Linux 프로세스 전환 시간은 스레드 스위치에 매우 가깝습니다).
threading
앓고 파이썬에서 두 가지 주요 단점 .
물론 하나는 특정 구현입니다. --- 대부분 CPython에 영향을 미칩니다. 그것이 GIL입니다. 대부분의 경우 대부분의 CPython 프로그램은 두 개 이상의 CPU (코어) 가용성의 이점을 얻지 못하며 종종 GIL 잠금 경합으로 인해 성능이 저하 됩니다.
구현과 관련이없는 더 큰 문제는 스레드가 동일한 메모리, 신호 처리기, 파일 설명자 및 특정 기타 OS 리소스를 공유한다는 것입니다. 따라서 프로그래머는 개체 잠금, 예외 처리 및 전체 프로세스 (스레드 모음)를 종료, 중단 또는 교착 상태로 만들 수있는 코드의 기타 측면에 대해 매우주의해야합니다.
비교해 보면 multiprocessing
모델은 각 프로세스에 자체 메모리, 파일 설명자 등을 제공합니다. 그중 하나에서 충돌 또는 처리되지 않은 예외는 해당 리소스 만 죽이고 자식 또는 형제 프로세스의 사라짐을 강력하게 처리하는 것이 디버깅, 격리보다 훨씬 쉬울 수 있습니다. 스레드에서 유사한 문제를 수정하거나 해결합니다.
- (Note: use of
threading
with major Python systems, such as NumPy, may suffer considerably less from GIL contention than most of your own Python code would. That's because they've been specifically engineered to do so; the native/binary portions of NumPy, for example, will release the GIL when that's safe).
The twisted option:
It's also worth noting that Twisted offers yet another alternative which is both elegant and very challenging to understand. Basically, at the risk of over simplifying to the point where fans of Twisted may storm my home with pitchforks and torches, Twisted provides event-driven co-operative multi-tasking within any (single) process.
To understand how this is possible one should read about the features of select()
(which can be built around the select() or poll() or similar OS system calls). Basically it's all driven by the ability to make a request of the OS to sleep pending any activity on a list of file descriptors or some timeout.
Awakening from each of these calls to select()
is an event --- either one involving input available (readable) on some number of sockets or file descriptors, or buffering space becoming available on some other (writable) descriptors or sockets, some exceptional conditions (TCP out-of-band PUSH'd packets, for example), or a TIMEOUT.
Thus the Twisted programming model is built around handling these events then looping on the resulting "main" handler, allowing it to dispatch the events to your handlers.
I personally think of the name, Twisted as evocative of the programming model ... since your approach to the problem must be, in some sense, "twisted" inside out. Rather than conceiving of your program as a series of operations on input data and outputs or results, you're writing your program as a service or daemon and defining how it reacts to various events. (In fact the core "main loop" of a Twisted program is (usually? always?) a reactor()
).
The major challenges to using Twisted involve twisting your mind around the event driven model and also eschewing the use of any class libraries or toolkits which are not written to co-operate within the Twisted framework. This is why Twisted supplies its own modules for SSH protocol handling, for curses, and its own subprocess/Popen functions, and many other modules and protocol handlers which, at first blush, would seem to duplicate things in the Python standard libraries.
I think it's useful to understand Twisted on a conceptual level even if you never intend to use it. It may give insights into performance, contention, and event handling in your threading, multiprocessing and even subprocess handling as well as any distributed processing you undertake.
(Note: Newer versions of Python 3.x are including asyncio (asynchronous I/O) features such as async def, the @async.coroutine decorator, and the await keyword, and yield from future support. All of these are roughly similar to Twisted from a process (co-operative multitasking) perspective). (For the current status of Twisted support for Python 3, check out: https://twistedmatrix.com/documents/current/core/howto/python3.html)
The distributed option:
Yet another realm of processing you haven't asked about, but which is worth considering, is that of distributed processing. There are many Python tools and frameworks for distributed processing and parallel computation. Personally I think the easiest to use is one which is least often considered to be in that space.
It is almost trivial to build distributed processing around Redis. The entire key store can be used to store work units and results, Redis LISTs can be used as Queue()
like object, and the PUB/SUB support can be used for Event
-like handling. You can hash your keys and use values, replicated across a loose cluster of Redis instances, to store the topology and hash-token mappings to provide consistent hashing and fail-over for scaling beyond the capacity of any single instance for co-ordinating your workers and marshaling data (pickled, JSON, BSON, or YAML) among them.
Of course as you start to build a larger scale and more sophisticated solution around Redis you are re-implementing many of the features that have already been solved using, Celery, Apache Spark and Hadoop, Zookeeper, etcd, Cassandra and so on. Those all have modules for Python access to their services.
[Update: A couple of resources for consideration if you're considering Python for computationally intensive across distributed systems: IPython Parallel and PySpark. While these are general purpose distributed computing systems, they are particularly accessible and popular subsystems data science and analytics].
Conclusion
There you have the gamut of processing alternatives for Python, from single threaded, with simple synchronous calls to sub-processes, pools of polled subprocesses, threaded and multiprocessing, event-driven co-operative multi-tasking, and out to distributed processing.
In a similar case I opted for separate processes and the little bit of necessary communication trough network socket. It is highly portable and quite simple to do using python, but probably not the simpler (in my case I had also another constraint: communication with other processes written in C++).
In your case I would probably go for multiprocess, as python threads, at least when using CPython, are not real threads. Well, they are native system threads but C modules called from Python may or may not release the GIL and allow other threads them to run when calling blocking code.
To use multiple processors in CPython your only choice is the multiprocessing
module. CPython keeps a lock on it's internals (the GIL) which prevents threads on other cpus to work in parallel. The multiprocessing
module creates new processes ( like subprocess
) and manages communication between them.
Shell out and let the unix out to do your jobs:
use iterpipes to wrap subprocess and then:
INPUTS_FROM_YOU | xargs -n1 -0 -P NUM ./process #NUM parallel processes
OR
Gnu Parallel will also serve
You hang out with GIL while you send the backroom boys out to do your multicore work.
'program story' 카테고리의 다른 글
영업일 계산 (0) | 2020.08.20 |
---|---|
pycharm은 탭을 공백으로 자동으로 변환합니다. (0) | 2020.08.20 |
Razor / MVC3를 사용하여 AssemblyVersion을 웹 페이지로 가져 오는 데 문제가 있습니다. (0) | 2020.08.20 |
두 개의 복잡한 개체를 비교하는 가장 좋은 방법 (0) | 2020.08.20 |
'URL'에서 'createObjectURL'실행 실패 : (0) | 2020.08.20 |