동시성 및 병렬성으로 Python 코드를 향상하는 방법

주요 시사점

  • 동시성과 병렬성은 컴퓨팅 작업 실행의 기본 원칙이며 각각 고유한 특성을 가지고 있습니다.
  • 동시성은 효율적인 리소스 활용과 향상된 애플리케이션 응답성을 가능하게 하며, 병렬성은 최적의 성능과 확장성을 위해 매우 중요합니다.
  • Python은 asyncio를 사용한 스레딩 및 비동기 프로그래밍과 같은 동시성 처리 옵션과 multiprocessing 모듈을 사용한 병렬 처리를 위한 옵션을 제공합니다.

동시성과 병렬성은 여러 프로그램을 동시에 실행할 수 있는 두 가지 기술입니다. Python에는 작업을 동시 및 병렬로 처리하기 위한 여러 옵션이 있어 혼란스러울 수 있습니다.

Python에서 동시성과 병렬성을 적절하게 구현하는 데 사용할 수 있는 도구와 라이브러리를 살펴보고 이들의 차이점을 살펴보세요.

동시성과 병렬성의 이해

동시성과 병렬성은 컴퓨팅에서 작업 실행의 두 가지 기본 원칙을 나타냅니다. 각각은 고유한 특성을 가지고 있습니다.

  • 동시성은 여러 작업을 동시에 실행하지 않고도 동시에 관리할 수 있는 프로그램의 기능입니다. 이는 작업을 인터리빙하여 동시에 나타나는 방식으로 작업을 전환한다는 아이디어를 중심으로 진행됩니다.
  • 반면 병렬성은 여러 작업을 실제로 병렬로 실행하는 것을 의미합니다. 일반적으로 여러 CPU 코어 또는 프로세서를 활용합니다. 병렬 처리는 진정한 동시 실행을 달성하여 작업을 더 빠르게 수행할 수 있게 하며 계산 집약적인 작업에 적합합니다.
  • 동시성과 병렬성의 중요성

    컴퓨팅에서 동시성과 병렬성의 필요성은 아무리 강조해도 지나치지 않습니다. 이러한 기술이 중요한 이유는 다음과 같습니다.

  • 리소스 활용: 동시성을 통해 시스템 리소스를 효율적으로 활용하여 작업이 외부 리소스를 기다리지 않고 적극적으로 진행되도록 할 수 있습니다.
  • 응답성: 동시성은 특히 사용자 인터페이스나 웹 서버와 관련된 시나리오에서 애플리케이션의 응답성을 향상시킬 수 있습니다.
  • 성능: 병렬성은 특히 복잡한 계산, 데이터 처리 및 시뮬레이션과 같은 CPU 바인딩 작업에서 최적의 성능을 달성하는 데 중요합니다.
  • 확장성: 확장 가능한 시스템을 구축하려면 동시성과 병렬성이 모두 필수적입니다.
  • 미래 보장: 하드웨어 추세가 계속해서 멀티코어 프로세서를 선호함에 따라 병렬성을 활용하는 기능의 필요성이 점점 더 커질 것입니다.
  •   모든 5G가 동일한 것은 아닙니다: 밀리미터파, 저대역 및 중대역 설명

    Python의 동시성

    asyncio 라이브러리를 사용한 스레딩 및 비동기 프로그래밍을 사용하여 Python에서 동시성을 달성할 수 있습니다.

    Python의 스레딩

    스레딩은 단일 프로세스 내에서 작업을 생성하고 관리할 수 있는 Python 동시성 메커니즘입니다. 스레드는 특정 유형의 작업, 특히 I/O 바인딩되어 동시 실행의 이점을 누릴 수 있는 작업에 적합합니다.

    Python의 스레딩 모듈 스레드 생성 및 관리를 위한 고급 인터페이스를 제공합니다. GIL(Global Interpreter Lock)은 진정한 병렬성 측면에서 스레드를 제한하지만 작업을 효율적으로 인터리브하여 동시성을 달성할 수 있습니다.

    아래 코드는 스레드를 사용한 동시성 구현의 예를 보여줍니다. Python 요청 라이브러리를 사용하여 일반적인 I/O 차단 작업인 HTTP 요청을 보냅니다. 또한 실행 시간을 계산하기 위해 time 모듈을 사용합니다.

     import requests
    import time
    import threading

    urls = [
        'https://www.google.com',
        'https://www.wikipedia.org',
        'https://www.makeuseof.com',
    ]


    def download_url(url):
        response = requests.get(url)
        print(f"Downloaded {url} - Status Code: {response.status_code}")


    start_time = time.time()

    for url in urls:
        download_url(url)

    end_time = time.time()
    print(f"Sequential download took {end_time - start_time:.2f} seconds\n")


    start_time = time.time()
    threads = []

    for url in urls:
        thread = threading.Thread(target=download_url, args=(url,))
        thread.start()
        threads.append(thread)


    for thread in threads:
        thread.join()

    end_time = time.time()
    print(f"Threaded download took {end_time - start_time:.2f} seconds")

    이 프로그램을 실행하면 스레드 요청이 순차 요청보다 얼마나 빠른지 확인할 수 있습니다. 그 차이는 1초 미만에 불과하지만 I/O 중심 작업에 스레드를 사용하면 성능이 향상되는 것을 확실히 느낄 수 있습니다.

    Asyncio를 사용한 비동기 프로그래밍

    비동기 코루틴이라는 비동기 작업을 관리하는 이벤트 루프를 제공합니다. 코루틴은 일시 중지하고 재개할 수 있는 함수이므로 I/O 바인딩 작업에 이상적입니다. 라이브러리는 네트워크 요청과 같은 외부 리소스를 기다리는 작업이 포함된 시나리오에 특히 유용합니다.

    asyncio를 사용하도록 이전 요청 전송 예제를 수정할 수 있습니다.

     import asyncio
    import aiohttp
    import time

    urls = [
        'https://www.google.com',
        'https://www.wikipedia.org',
        'https://www.makeuseof.com',
    ]


    async def download_url(url):
        async with aiohttp.ClientSession() as session:
            async with session.get(url) as response:
                content = await response.text()
                print(f"Downloaded {url} - Status Code: {response.status}")


    async def main():
        
        tasks = [download_url(url) for url in urls]

        
        await asyncio.gather(*tasks)

    start_time = time.time()


    asyncio.run(main())

    end_time = time.time()

    print(f"Asyncio download took {end_time - start_time:.2f} seconds")

    코드를 사용하면 asyncio를 사용하여 웹 페이지를 동시에 다운로드하고 비동기 I/O 작업을 활용할 수 있습니다. 이는 I/O 바인딩 작업에 대한 스레딩보다 더 효율적일 수 있습니다.

      제품 데이터를 효과적으로 관리하기 위한 15가지 최고의 PIM 소프트웨어

    Python의 병렬성

    다음을 사용하여 병렬성을 구현할 수 있습니다. Python의 다중 처리 모듈이를 통해 멀티코어 프로세서를 최대한 활용할 수 있습니다.

    Python의 다중 처리

    Python의 다중 처리 모듈은 각각 고유한 Python 인터프리터와 메모리 공간을 갖춘 별도의 프로세스를 생성하여 병렬 처리를 달성하는 방법을 제공합니다. 이는 GIL(Global Interpreter Lock)을 효과적으로 우회하므로 CPU 바인딩 작업에 적합합니다.

     import requests
    import multiprocessing
    import time

    urls = [
        'https://www.google.com',
        'https://www.wikipedia.org',
        'https://www.makeuseof.com',
    ]


    def download_url(url):
        response = requests.get(url)
        print(f"Downloaded {url} - Status Code: {response.status_code}")

    def main():
        
        num_processes = len(urls)
        pool = multiprocessing.Pool(processes=num_processes)

        start_time = time.time()
        pool.map(download_url, urls)
        end_time = time.time()

        
        pool.close()
        pool.join()

        print(f"Multiprocessing download took {end_time-start_time:.2f} seconds")

    main()

    이 예에서 다중 처리는 여러 프로세스를 생성하여 download_url 함수가 병렬로 실행될 수 있도록 합니다.

    동시성 또는 병렬성을 사용해야 하는 경우

    동시성과 병렬성 사이의 선택은 작업의 성격과 사용 가능한 하드웨어 리소스에 따라 달라집니다.

    파일 읽기 및 쓰기, 네트워크 요청 등 I/O 바인딩 작업을 처리할 때와 메모리 제약이 문제가 될 때 동시성을 사용할 수 있습니다.

    진정한 병렬 처리의 이점을 누릴 수 있는 CPU 바인딩된 작업이 있고 한 작업의 실패가 다른 작업에 영향을 주지 않아야 하는 작업 간에 강력한 격리가 있는 경우 다중 처리를 사용합니다.

    동시성과 병렬성을 활용하세요

    병렬성과 동시성은 Python 코드의 응답성과 성능을 향상시키는 효과적인 방법입니다. 이러한 개념 간의 차이점을 이해하고 가장 효과적인 전략을 선택하는 것이 중요합니다.

    Python은 CPU 바인딩된 프로세스로 작업하든 I/O 바인딩된 프로세스로 작업하든 상관없이 동시성 또는 병렬성을 통해 코드를 더욱 효과적으로 만드는 데 필요한 도구와 모듈을 제공합니다.