source

멀티프로세싱과 스레딩 Python

bestscript 2022. 11. 12. 08:32

멀티프로세싱과 스레딩 Python

스레드화비해 멀티프로세싱의 장점을 이해하려고 합니다.멀티프로세싱이 Global Interpreter Lock을 우회하는 것은 알고 있습니다만, 그 밖에 어떤 장점이 있습니까?스레딩은 같은 기능을 하지 않을 수 있습니까?

여기 제가 생각해낸 몇 가지 장단점이 있습니다.

멀티프로세서

장점

  • 개별 메모리 공간
  • 코드는 보통 간단하다.
  • 여러 CPU와 코어를 활용
  • cPython에 대한 GIL 제한 회피
  • 공유 메모리를 사용하는 경우를 제외하고 동기 프리미티브에 대한 대부분의 필요성을 배제합니다(대신 IPC용 통신 모델에 가깝습니다).
  • 자프로세스의 중단/정지 가능
  • Python 이 python multiprocessing와 매우 되어 있습니다.threading.Thread
  • CPU 바인드 처리를 위해 cPython이 반드시 필요합니다.

단점

  • IPC는 조금 복잡하여 오버헤드가 증가 (통신 모델과 공유 메모리/개체 비교)
  • 대용량 메모리 설치 공간

스레드화

장점

  • 경량 - 메모리 설치 공간 절감
  • 공유 메모리 - 다른 컨텍스트에서 상태로의 접근을 용이하게 합니다.
  • 응답성이 뛰어난 UI를 쉽게 만들 수 있습니다.
  • GIL을 올바르게 릴리스하는 cPython C 확장 모듈은 병렬로 실행됩니다.
  • I/O바인드 어플리케이션

단점

  • cPython - GIL 대상
  • 인터럽트 불가/킬 불가
  • 큐('/' )Queue입니다(잠금합니다). ) ( module module기 module(()((((((((((((((((((((((( ( module 。
  • 일반적으로 코드를 이해하고 올바르게 파악하기 어렵습니다. 레이스 조건의 가능성이 극적으로 증가합니다.

threading에서는 스레드, 즉 '스루드'를 합니다.multiprocessing이치노차이점은 스레드는 동일한 메모리 공간에서 실행되지만 프로세스에는 별도의 메모리가 있다는 것입니다.이 때문에 멀티프로세싱에서는 프로세스 간에 오브젝트를 공유하기가 다소 어려워집니다.스레드는 같은 메모리를 사용하기 때문에 주의가 필요합니다.그렇지 않으면 2개의 스레드가 동시에 같은 메모리에 기입됩니다.이것이 글로벌 인터프리터 잠금이 필요한 이유입니다.

산란 프로세스는 산란 스레드보다 약간 느립니다.

스레딩의 역할은 응용 프로그램이 응답할 수 있도록 하는 것입니다.데이터베이스 연결이 있고 사용자 입력에 응답해야 한다고 가정합니다.스레드를 사용하지 않으면 데이터베이스 연결이 사용 중인 경우 응용 프로그램은 사용자에게 응답할 수 없습니다.데이터베이스 연결을 별도의 스레드로 분할하여 응용 프로그램의 응답성을 높일 수 있습니다.또한 두 스레드는 동일한 프로세스에 있기 때문에 동일한 데이터 구조(뛰어난 성능 및 유연한 소프트웨어 설계)에 액세스할 수 있습니다.

GIL을 통해 앱은 실제로 두 가지 작업을 동시에 수행하는 것이 아니라 데이터베이스의 리소스 잠금을 별도의 스레드에 배치하여 CPU 시간과 사용자 상호 작용 간에 전환할 수 있습니다.스레드 간에 CPU 시간이 할당됩니다.

멀티프로세싱은 특정 시간에 여러 가지 작업을 수행하고자 할 때 사용합니다.애플리케이션이 6개의 데이터베이스에 연결하여 각 데이터 세트에 대해 복잡한 매트릭스 변환을 수행해야 한다고 가정합니다.각 작업을 다른 스레드에 배치하면 어느 정도 도움이 될 수 있습니다.한 접속이 아이돌 상태일 때 다른 접속이 CPU 시간을 얻을 수 있기 때문입니다.그러나 GIL은 한 CPU의 자원만을 사용한다는 것을 의미하기 때문입니다.각 작업을 Multiprocessing 프로세스에 포함시킴으로써 각 작업을 자체 CPU로 실행하고 풀로 실행할 수 있습니다.l 효율화

Python 문서 견적

이 답변의 정식 버전은 현재 중복된 질문입니다.스레드 모듈과 멀티프로세서 모듈의 차이점은 무엇입니까?

프로세스 vs 스레드 및 GIL에 대한 Python 문서의 주요 인용문을 강조 표시했습니다: CPython의 글로벌 인터프리터 잠금(GIL)이란?

프로세스와 스레드 실험

그 차이를 좀 더 구체적으로 보여주기 위해 벤치마킹을 조금 해봤습니다.

벤치마크에서는 8 하이퍼스레드 CPU의 다양한 스레드 수에 대해 CPU와 IO바인드 작업의 타이밍을 설정했습니다.스레드당 공급되는 작업은 항상 동일하기 때문에 스레드 수가 많을수록 공급되는 총 작업량이 많아집니다.

결과는 다음과 같습니다.

여기에 이미지 설명 입력

데이터를 표시합니다.

결론:

  • CPU 바운드 작업의 경우 멀티프로세싱은 항상 고속입니다.아마도 GIL에 의해

  • I/O 바인딩 작업. 둘 다 정확히 같은 속도입니다.

  • 스레드는 8개의 하이퍼스레드 머신에 있기 때문에 예상되는 8배에서 최대 4배까지만 확장됩니다.

    예상되는 8배의 속도 향상에 도달하는 C POSIX CPU 바운드 작업과 대조합니다.시간(1)의 출력에서 'real', 'user' 및 'sys'는 무엇을 의미합니까?

    TODO: 이유는 모르겠지만 Python의 비효율적인 부분이 또 있을 것입니다.

테스트 코드:

#!/usr/bin/env python3

import multiprocessing
import threading
import time
import sys

def cpu_func(result, niters):
    '''
    A useless CPU bound function.
    '''
    for i in range(niters):
        result = (result * result * i + 2 * result * i * i + 3) % 10000000
    return result

class CpuThread(threading.Thread):
    def __init__(self, niters):
        super().__init__()
        self.niters = niters
        self.result = 1
    def run(self):
        self.result = cpu_func(self.result, self.niters)

class CpuProcess(multiprocessing.Process):
    def __init__(self, niters):
        super().__init__()
        self.niters = niters
        self.result = 1
    def run(self):
        self.result = cpu_func(self.result, self.niters)

class IoThread(threading.Thread):
    def __init__(self, sleep):
        super().__init__()
        self.sleep = sleep
        self.result = self.sleep
    def run(self):
        time.sleep(self.sleep)

class IoProcess(multiprocessing.Process):
    def __init__(self, sleep):
        super().__init__()
        self.sleep = sleep
        self.result = self.sleep
    def run(self):
        time.sleep(self.sleep)

if __name__ == '__main__':
    cpu_n_iters = int(sys.argv[1])
    sleep = 1
    cpu_count = multiprocessing.cpu_count()
    input_params = [
        (CpuThread, cpu_n_iters),
        (CpuProcess, cpu_n_iters),
        (IoThread, sleep),
        (IoProcess, sleep),
    ]
    header = ['nthreads']
    for thread_class, _ in input_params:
        header.append(thread_class.__name__)
    print(' '.join(header))
    for nthreads in range(1, 2 * cpu_count):
        results = [nthreads]
        for thread_class, work_size in input_params:
            start_time = time.time()
            threads = []
            for i in range(nthreads):
                thread = thread_class(work_size)
                threads.append(thread)
                thread.start()
            for i, thread in enumerate(threads):
                thread.join()
            results.append(time.time() - start_time)
        print(' '.join('{:.6e}'.format(result) for result in results))

GitHub 업스트림 + 동일한 디렉토리에 코드를 표시합니다.

Ubuntu 18.10, Python 3.6.7에서 CPU 탑재 Lenovo ThinkPad P51 노트북으로 테스트 완료: 인텔 Core i7-7820HQ CPU(4코어/8스레드), RAM: Samsung M471A2K43BB1-CRC(16GiB×2), SSD: Samsung MZVLB512HAJQ-000L7(3,000MB/s)

지정된 시간에 실행 중인 스레드 시각화

이 게시물 https://rohanvarma.me/GIL/에서는 다음과 같은 인수를 사용하여 스레드가 일정될 때마다 콜백을 실행할 수 있다는 것을 배웠습니다.multiprocessing.Process.

이를 통해 각 시간에 실행되는 스레드를 정확하게 볼 수 있습니다.이 작업을 마치면 (이 그래프는 제가 작성했습니다) 다음과 같이 표시됩니다.

            +--------------------------------------+
            + Active threads / processes           +
+-----------+--------------------------------------+
|Thread   1 |********     ************             |
|         2 |        *****            *************|
+-----------+--------------------------------------+
|Process  1 |***  ************** ******  ****      |
|         2 |** **** ****** ** ********* **********|
+-----------+--------------------------------------+
            + Time -->                             +
            +--------------------------------------+

그 결과는 다음과 같습니다.

  • 스레드는 GIL에 의해 완전히 시리얼화됩니다.
  • 프로세스를 병렬로 실행할 수 있습니다.

주요 장점은 격리입니다.크래시 프로세스는 다른 프로세스를 정지시키지 않지만 크래시 스레드는 다른 스레드에 큰 피해를 줄 수 있습니다.

질문에서 언급한 바와 같이 Python에서의 멀티프로세싱은 진정한 병렬화를 실현하는 유일한 방법입니다.GIL에 의해 스레드가 병렬로 실행되지 않기 때문에 멀티스레딩에서는 이 작업을 수행할 수 없습니다.

그 결과, 스레드는 Python에서 항상 유용하지는 않을 수 있으며, 실제로 무엇을 달성하려고 하는지에 따라 성능이 저하될 수도 있습니다.예를 들어 gzip 파일의 압축 해제나 3D렌더링(CPU 부하가 높은 모든 것) 등의 CPU 바인드 작업을 수행하는 경우 스레드화는 실제로 성능을 저해할 수 있습니다.이 경우 이 방법만 실제로 병렬로 실행되며 작업 가중치를 분산하는 데 도움이 되므로 다중 처리를 사용할 수 있습니다.멀티프로세싱에서는 스크립트의 메모리를 각 서브프로세스에 카피하는 것으로, 큰 사이즈의 애플리케이션에 문제가 발생할 가능성이 있기 때문에, 이것에 약간의 오버헤드가 발생할 가능성이 있습니다.

그러나 작업이 IO 바인딩된 경우에는 멀티스레딩이 유용합니다.예를 들어 대부분의 태스크가 API 을 기다리는 경우 멀티스레딩을 사용합니다.이는 CPU를 가만히 두는 것이 아니라 기다리는 동안 다른 스레드에서 다른 요청을 시작하는 것이 좋기 때문입니다.

TL;DR

  • 멀티스레딩은동시에실행되며IO바운드작업에사용됩니다.
  • 멀티프로세싱은 진정한 병렬화를 실현하며 CPU에 바인딩된 태스크에 사용됩니다.

또 다른 언급은 속도와 관련된 OS에 따라 다르다는 것입니다.윈도 프로세스는 비용이 많이 들기 때문에 윈도에서는 스레드가 더 낫지만 유닉스 프로세스는 윈도 버전보다 더 빠르기 때문에 유닉스 프로세스를 사용하는 것이 훨씬 안전하고 빠르게 생성됩니다.

다른 답변은 멀티스레딩과 멀티프로세싱 양상에 더 초점을 맞추고 있지만 파이썬 글로벌 인터프리터 잠금(GIL)을 고려해야 합니다.더 많은 수의 스레드(를 들어 k)가 생성되어도 1개의 스레드 어플리케이션으로 동작하기 때문에 일반적으로 퍼포먼스는 k배 향상되지 않습니다.GIL은 모든 것을 잠그고 단일 코어만을 사용하여 단일 스레드만 실행할 수 있는 글로벌 잠금입니다.numpy, Network, I/O 등 백그라운드 작업이 많이 이루어지고 GIL이 출시되는 C 확장 기능을 사용하는 곳에서는 성능이 향상됩니다.
따라서 스레드를 사용할 경우 운영체제레벨 스레드는 1개뿐이지만 파이썬은 자체 스레드에 의해 완전히 관리되지만 기본적으로는 단일 프로세스로 실행됩니다.프리엠프션은 이러한 의사 스레드 사이에서 발생합니다.CPU가 최대 용량으로 동작하는 경우는, 멀티 프로세싱으로 전환할 수 있습니다.
이제 자체 포함 실행 인스턴스의 경우 풀을 선택할 수 있습니다.그러나 데이터가를 사용하여 해야 합니다.multiprocessing.Process.

멀티프로세서

  • 멀티프로세싱은 CPU를 추가하여 처리 능력을 높입니다.
  • 여러 프로세스가 동시에 실행됩니다.
  • 프로세스 생성에는 많은 시간과 리소스가 소요됩니다.
  • 다중 처리는 대칭 또는 비대칭일 수 있습니다.
  • Python의 멀티프로세서 라이브러리는 별도의 메모리 공간, 여러 CPU 코어를 사용하며, CPython의 GIL 제한을 우회합니다.자녀 프로세스는 킬 가능(예: 프로그램 내 함수 호출)하여 훨씬 사용하기 쉽습니다.
  • 모듈의 몇 가지 주의사항은 메모리 용량이 크다는 것입니다.IPC는 조금 복잡하지만 오버헤드는 커집니다.

멀티스레딩

  • 멀티스레딩은 단일 프로세스의 여러 스레드를 생성하여 컴퓨팅 성능을 향상시킵니다.
  • 단일 프로세스의 여러 스레드가 동시에 실행됩니다.
  • 스레드 작성은 시간과 자원 면에서 모두 경제적입니다.
  • 멀티스레딩 라이브러리는 가볍고 메모리를 공유하며 응답성 UI를 담당하며 I/O 바인딩된 애플리케이션에 적합합니다.
  • 모듈은 죽일 수 없으며 GIL의 대상이 됩니다.
  • 여러 스레드가 동일한 공간에서 동일한 프로세스에 존재하며 각 스레드는 특정 작업을 수행하고 자체 코드, 자체 스택 메모리, 명령 포인터 및 공유 힙 메모리를 가집니다.
  • 스레드에 메모리 누설이 있는 경우 다른 스레드와 부모 프로세스가 손상될 수 있습니다.

Python을 사용한 멀티스레딩 및 멀티프로세싱의 예

Python 3에는 병렬 태스크 실행 기능이 있습니다.이것은 우리의 일을 더 쉽게 해준다.

스레드 풀링 및 프로세스 풀링에 사용됩니다.

다음은 통찰력을 제공합니다.

ThreadPoolExecutor의 예시

import concurrent.futures
import urllib.request

URLS = ['http://www.foxnews.com/',
        'http://www.cnn.com/',
        'http://europe.wsj.com/',
        'http://www.bbc.co.uk/',
        'http://some-made-up-domain.com/']

# Retrieve a single page and report the URL and contents
def load_url(url, timeout):
    with urllib.request.urlopen(url, timeout=timeout) as conn:
        return conn.read()

# We can use a with statement to ensure threads are cleaned up promptly
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
    # Start the load operations and mark each future with its URL
    future_to_url = {executor.submit(load_url, url, 60): url for url in URLS}
    for future in concurrent.futures.as_completed(future_to_url):
        url = future_to_url[future]
        try:
            data = future.result()
        except Exception as exc:
            print('%r generated an exception: %s' % (url, exc))
        else:
            print('%r page is %d bytes' % (url, len(data)))

프로세스 풀 실행기

import concurrent.futures
import math

PRIMES = [
    112272535095293,
    112582705942171,
    112272535095293,
    115280095190773,
    115797848077099,
    1099726899285419]

def is_prime(n):
    if n % 2 == 0:
        return False

    sqrt_n = int(math.floor(math.sqrt(n)))
    for i in range(3, sqrt_n + 1, 2):
        if n % i == 0:
            return False
    return True

def main():
    with concurrent.futures.ProcessPoolExecutor() as executor:
        for number, prime in zip(PRIMES, executor.map(is_prime, PRIMES)):
            print('%d is prime: %s' % (number, prime))

if __name__ == '__main__':
    main()

스레드는 동일한 메모리 공간을 공유하여 두 스레드가 동일한 메모리 위치를 공유하지 않도록 하기 위해 특별한 예방 조치를 취해야 합니다. CPython 인터프리터는 다음과 같은 메커니즘을 사용하여 이를 처리합니다.GIL또는 글로벌 인터프리터 잠금

GIL(GIL을 명확히 하고 싶을 뿐)이 무엇입니까?

CPython에서 글로벌 인터프리터 잠금(GIL)은 Python 개체에 대한 액세스를 보호하는 뮤텍스이므로 여러 스레드가 Python 바이트코드를 동시에 실행할 수 없습니다.이 잠금이 필요한 것은 주로 CPython의 메모리 관리가 스레드 세이프가 아니기 때문입니다.

주요 질문에서는 사용 사례를 어떻게 비교할 수 있습니까?

1-스레딩에 대한 사용 사례: GUI 프로그램 스레딩을 사용하여 응용 프로그램의 응답성을 높일 수 있습니다.예를 들어 텍스트 편집 프로그램에서는 하나의 스레드가 사용자 입력을 기록하고 다른 스레드는 텍스트를 표시하고 다른 스레드는 맞춤법 검사를 수행할 수 있습니다.여기서 프로그램은 사용자의 상호작용을 기다려야 합니다.가장 큰 병목 현상입니다.스레드의 또 다른 사용 사례는 웹 스크래퍼와 같이 IO 또는 네트워크에 바인딩된 프로그램입니다.

2-멀티프로세싱의 사용 예: 프로그램에서 CPU를 많이 사용하고 IO나 사용자 조작을 할 필요가 없는 경우 멀티프로세싱은 스레드 처리보다 우수합니다.

상세한 것에 대하여는, 링크와 링크를 참조해 주세요.또, 멀티 프로세싱에 대해서는, 여기를 참조해 주세요.

프로세스에는 여러 스레드가 있을 수 있습니다.이러한 스레드는 메모리를 공유할 수 있으며 프로세스 내의 실행 단위입니다.

프로세스는 CPU 상에서 실행되므로 스레드는 각 프로세스 아래에 있습니다.프로세스는 독립적으로 실행되는 개별 엔티티입니다. 메모리 할 수 .Cache(redis, memcache),Files , " " "Database.

내가 대학에서 배운 것처럼 위의 답들은 대부분 옳다.PRITISE에서는 (항상 python을 사용) 여러 스레드를 산란하는 것은 하나의 프로세스를 산란하는 것과 같습니다.다른 점은 하나의 코어만으로 모든 것을 100% 처리하는 것이 아니라 여러 코어가 부하를 분담한다는 것입니다.따라서 예를 들어 4코어 PC에서 10개의 스레드를 생성하면 CPU 전력의 25%밖에 얻을 수 없습니다.10개의 프로세스를 생성하면 CPU 처리가 100%가 됩니다(다른 제한이 없는 경우).저는 모든 신기술에 정통한 것은 아닙니다.나는 실제 경험을 가지고 대답한다.

언급URL : https://stackoverflow.com/questions/3044580/multiprocessing-vs-threading-python