조사하게된 배경
https://makenow90.tistory.com/66
병렬 처리의 세 가지 주요 개념: 비동기 처리, 멀티스레드, 멀티코어
병렬 처리는 작업의 특성에 따라 적합한 방식이 다릅니다. I/O 병렬 처리는 주로 입출력 대기 시간이 많은 작업에서, 멀티스레드는 경량 작업이나 I/O 바운드 작업에서, 그리고 멀티코어는 CPU 집약적인 작업에서 각각 효율적입니다. 여기에서는 각 방식의 특징과, 적절한 코드 예시를 설명하겠습니다.
1. 비동기 처리
비동기 처리란, 작업이 완료될 때까지 기다리지 않고 다른 작업을 계속 진행하는 방식을 의미합니다.
이는 I/O 작업, 네트워크 요청, 파일 읽기/쓰기 등 대기 시간이 발생하는 작업에서 효율적으로 사용됩니다.
Python에서는 asyncio, aiohttp, **aiofiles**와 같은 라이브러리를 통해 비동기 처리를 구현할 수 있습니다.
특징:
- 동시성(Concurrency):
- 여러 작업이 같은 이벤트 루프에서 병렬적으로 실행되는 것처럼 보입니다.
- 물리적 병렬성(Parallelism)과는 다르며, 단일 스레드에서도 구현할 수 있습니다.
- I/O 바운드 작업에 적합:
- 네트워크 호출, 파일 읽기/쓰기, 데이터베이스 쿼리 등 I/O 대기 시간이 긴 작업에 효과적입니다.
- I/O 작업은 CPU가 작업을 완료하지 않고 대기하는 시간이 많습니다.
- 네트워크 요청: 서버로 요청을 보낸 후 응답을 기다리는 시간.
- 파일 읽기/쓰기: 디스크에서 데이터를 로드하거나 저장하는 대기 시간.
- 비동기 처리는 작업 대기 상태에서 CPU를 유휴 상태로 두지 않고, 다른 작업을 실행
- 싱글 스레드에서 동작 가능:
- 비동기 처리는 멀티스레드나 멀티프로세스 환경 없이도 효율적으로 작동합니다.
- GIL의 영향을 받지 않음:
- Python의 GIL(Global Interpreter Lock)은 CPU 바운드 작업에서 성능을 제한하지만, 비동기 처리는 GIL의 영향을 거의 받지 않습니다.
비동기 방식을 활용하면 좋은 코딩의 종류
- 웹 크롤링 및 데이터 스크래핑
- 수많은 웹사이트에서 데이터를 동시에 요청하고 응답을 처리하는 작업.
- 비동기 방식으로 요청 대기 시간을 효율적으로 활용.
- API 대량 호출
- 여러 API 엔드포인트를 동시에 호출하거나, 대규모 병렬 네트워크 요청을 처리하는 작업.
- 예: 금융 데이터, 날씨 데이터 등 대량의 실시간 데이터를 가져오는 작업.
- 대규모 파일 입출력
- 여러 파일을 동시에 읽거나 쓰는 작업.
- 데이터 처리 및 분석 파이프라인에서 자주 활용.
- 채팅 애플리케이션 및 실시간 스트리밍
- 다수의 클라이언트와 서버 간 메시지를 주고받는 작업.
- 예: 채팅, 라이브 스트리밍 애플리케이션에서의 실시간 데이터 처리.
- 데이터베이스 비동기 쿼리
- 대규모 비동기 데이터베이스 요청 처리.
- 비동기 ORM(예: SQLAlchemy Async)을 사용하여 효율적으로 데이터 읽기/쓰기.
적절한 코드 예시 1: (수천개의)비동기 파일 읽기
import asyncio
async def read_file_async(file):
async with aiofiles.open(file, 'r') as f:
contents = await f.read()
print(f"File {file} read.")
async def main():
files = ['file1.txt', 'file2.txt', 'file3.txt']
tasks = [read_file_async(file) for file in files]
await asyncio.gather(*tasks)
asyncio.run(main())
적절한 코드 예시 2: 비동기 네트워크 요청
import asyncio
import aiohttp
async def fetch_url(session, url):
async with session.get(url) as response:
return await response.text()
async def main():
urls = ['http://example.com', 'http://example.org', 'http://example.net']
async with aiohttp.ClientSession() as session:
tasks = [fetch_url(session, url) for url in urls]
responses = await asyncio.gather(*tasks)
print(responses)
asyncio.run(main())
사용 이유:
- 비동기 처리는 주로 네트워크 요청이나 파일 입출력과 같이 I/O 대기 시간이 큰 작업에서 성능을 향상시킬 수 있습니다. 비동기 방식으로 여러 요청을 동시에 처리하여 효율성을 높일 수 있습니다.
2. 멀티스레드(Multi-Threading)
멀티스레드는 하나의 프로세스 내에서 여러 스레드를 생성하여 작업을 병렬로 수행하는 방식입니다. 각 스레드는 동일한 메모리 공간을 공유하면서 동시에 실행됩니다.
특징:
- 스레드(Thread): 스레드는 프로세스 내의 실행 흐름으로, 여러 스레드가 하나의 프로세스 내에서 병렬로 작업을 수행합니다. 스레드는 경량 프로세스라고도 하며, 프로세스보다 생성과 관리가 더 간단하고 빠릅니다.
- I/O 바운드 작업에 적합: CPU가 바쁘지 않고 I/O 작업을 대기하는 시간이 많을 때, 여러 스레드를 사용해 대기 시간을 줄일 수 있습니다. 예를 들어, 파일 읽기, 네트워크 요청 등을 동시에 여러 스레드에서 처리하면, 작업의 전반적인 대기 시간이 줄어들게 됩니다.
- GIL(Global Interpreter Lock): Python에서 멀티스레딩은 CPU 바운드 작업에 대한 성능 향상이 제한적일 수 있습니다. Python의 GIL로 인해 한 번에 하나의 스레드만 Python 바이트코드를 실행할 수 있으므로, CPU 집약적인 작업에서는 병목이 발생할 수 있습니다.
멀티스레드를 활용하면 좋은 코딩의 종류
- 백그라운드 작업 처리
- UI 애플리케이션에서 사용자 인터페이스를 멈추지 않고 백그라운드에서 작업을 수행.
- 예: 파일 다운로드, 이미지 처리 등.
- 경량 데이터 처리
- 독립적인 데이터 처리 작업이 많고, 작업 간 간섭이 적은 경우.
- 예: 로그 파일 분석, 실시간 데이터 필터링.
- 서버 요청 처리
- 다중 클라이언트 요청을 처리하는 서버에서 각 요청을 독립적인 스레드로 처리.
- 예: 채팅 서버, 멀티스레드 웹 서버.
- 멀티태스킹 애플리케이션
- 여러 작업을 동시에 실행하는 프로그램.
- 예: 동시에 센서 데이터를 읽고 저장하거나, 사용자 입력을 처리하는 임베디드 시스템.
- 멀티미디어 처리
- 동영상 재생, 오디오 스트리밍, 이미지 렌더링 등 실시간 작업에서 멀티스레드로 성능 향상.
- 예: 프레임 처리, 필터 적용.
적절한 코드 예시 1: 파일 다운로드
from threading import Thread
import requests
def download_file(url):
response = requests.get(url)
filename = url.split("/")[-1]
with open(filename, 'wb') as f:
f.write(response.content)
print(f"Downloaded {filename}")
urls = ['http://example.com/file1', 'http://example.com/file2', 'http://example.com/file3']
threads = []
for url in urls:
thread = Thread(target=download_file, args=(url,))
thread.start()
threads.append(thread)
for thread in threads:
thread.join()
멀티스레드의 장점:
- I/O 바운드 작업에 매우 효율적: 네트워크 요청, 파일 읽기/쓰기와 같은 I/O 작업에서는 멀티스레딩을 통해 여러 작업을 동시에 처리할 수 있습니다.
- 경량 프로세싱: 스레드 간 문맥 교환이 프로세스보다 가볍기 때문에 오버헤드가 적습니다.
단점:
- GIL의 제약(Python): Python의 GIL로 인해, CPU 바운드 작업에서는 성능이 제한될 수 있습니다.
- 스레드 관리의 복잡성: 스레드가 많아질수록 동기화 문제(예: 데이터 레이스, 데드락) 등의 복잡한 상황이 발생할 수 있습니다.
적절한 코드 예시 2: 로그 파일 모니터링
from threading import Thread
def monitor_log(file):
with open(file, 'r') as f:
while True:
line = f.readline()
if line:
print(f"{file}: {line.strip()}")
log_files = ['log1.txt', 'log2.txt', 'log3.txt']
threads = []
for file in log_files:
thread = Thread(target=monitor_log, args=(file,))
thread.start()
threads.append(thread)
for thread in threads:
thread.join()
사용 이유:
- 멀티스레드는 네트워크 요청, 파일 읽기/쓰기와 같이 I/O 대기 시간이 많은 작업에서 성능을 높일 수 있습니다. 각 스레드가 독립적으로 실행되어 I/O 대기 중에도 다른 작업을 수행할 수 있기 때문에 효율적입니다.
3. 멀티프로세스
멀티프로세스는 하나의 프로그램에서 여러 프로세스를 병렬로 실행하여 작업을 처리하는 방식입니다.
프로세스(Process)는 독립된 실행 단위로, 각각의 프로세스는 독립적인 메모리 공간을 가지고 동작함
특징:
1) 프로세스란?
- 프로세스는 프로그램의 실행 인스턴스로, 운영 체제에서 관리되는 독립적인 실행 단위입니다.
- 각각의 프로세스는 고유한 메모리 공간을 할당받아 독립적으로 실행됩니다.
- 운영 체제의 스케줄러가 프로세스를 병렬로 실행합니다.
2) 멀티프로세스의 특징
장점
- GIL 제약 없음:
- Python의 GIL은 멀티스레드의 병렬성을 제한하지만, 멀티프로세스는 이를 우회하여 진정한 병렬 처리를 구현할 수 있습니다.
- 독립된 메모리 공간:
- 프로세스 간 메모리가 독립적이므로, 하나의 프로세스가 종료되거나 충돌해도 다른 프로세스에 영향을 주지 않습니다.
- 멀티코어 활용:
- 프로세스는 운영 체제가 제공하는 멀티코어를 활용하여 병렬로 실행되므로, CPU 자원을 최대한 활용할 수 있습니다.
단점
- 메모리 사용량 증가:
- 각 프로세스가 독립적인 메모리 공간을 가지므로, 멀티스레드보다 메모리 사용량이 많습니다.
- 통신 비용(IPC):
- 프로세스 간 데이터 공유나 통신이 필요할 경우 **Inter-Process Communication(IPC)**을 사용해야 하며, 이로 인해 오버헤드가 발생합니다.
- 그럼에도 IPC를 사용 하는 예: 대규모 데이터 처리에서 중간 결과 병합, 작업 상태 공유
- 데이터 교환과 동기화가 필요하다면, Queue, Pipe( 양방향 데이터 전달이 가능 ), Shared Memory( 프로세스 간 메모리를 공유하여 데이터를 전달 ) 와 같은 IPC 방식을 활용.
- 프로세스 생성 비용:
- 프로세스를 생성하는 데 더 많은 리소스와 시간이 필요합니다.
적절한 코드 예시 1: 데이터 분석 병렬 계산
from multiprocessing import Pool
import numpy as np
def compute_square(number):
return number ** 2
numbers = np.arange(1000000)
if __name__ == '__main__':
with Pool() as pool:
result = pool.map(compute_square, numbers)
print(result)
적절한 코드 예시 2: 이미지 처리
from multiprocessing import Pool
from PIL import Image
import os
def process_image(image_file):
img = Image.open(image_file)
img = img.rotate(90) # 이미지 회전
img.save(f"rotated_{os.path.basename(image_file)}")
return f"{image_file} processed."
image_files = ['image1.jpg', 'image2.jpg', 'image3.jpg']
if __name__ == '__main__':
with Pool() as pool:
results = pool.map(process_image, image_files)
print(results)
멀티프로세스를 활용하면 좋은 코딩의 종류
- CPU 집약적인 작업
- 복잡한 계산이나 대규모 데이터 처리를 병렬로 실행.
- 예: 머신 러닝 모델 훈련, 이미지/비디오 처리, 시뮬레이션 연산.
- 데이터 파이프라인 병렬 처리
- 데이터 분석 작업에서 대량의 데이터를 여러 프로세스에서 병렬로 처리.
- 예: 대규모 로그 분석, ETL(Extract, Transform, Load) 작업.
- 웹 크롤러와 스크래퍼의 대규모 병렬 처리
- 여러 프로세스에서 독립적으로 웹 페이지를 다운로드하거나 크롤링.
- 비동기 방식보다 프로세스를 활용하면 병렬성에서 이점을 얻음.
- 멀티유저 서버
- 고성능 서버에서 다수의 클라이언트 요청을 병렬로 처리.
- 예: HTTP 요청 처리, 게임 서버의 클라이언트별 독립된 세션 관리.
- 비동기 I/O와 CPU 작업 결합
- 비동기적으로 데이터를 읽어온 후, 데이터를 병렬로 처리.
- 예: 네트워크 데이터를 수집한 후 여러 프로세스에서 분석 및 저장.
정리:
- I/O 병렬 처리: 네트워크 요청, 파일 읽기/쓰기와 같은 I/O 바운드 작업에서 비동기 I/O나 멀티스레딩을 통해 성능을 향상시킵니다. 예: 비동기 파일 읽기, 비동기 네트워크 요청.
- 멀티스레드: I/O 대기 시간이 많은 작업에서 여러 스레드를 통해 동시에 처리하여 대기 시간을 최소화합니다. 예: 파일 다운로드, 로그 파일 모니터링.
- 멀티코어: CPU 집약적인 작업에서 여러 코어를 활용해 병렬 처리를 통해 성능을 극대화합니다. 예: 데이터 분석 병렬 계산, 이미지 처리.
병렬 처리 방식 비교 (I/O 병렬 처리, 멀티스레드, 멀티코어)
특징 | 비동기 I/O | 멀티스레드 | 멀티프로세스 |
---|---|---|---|
주요 사용 환경 | I/O 바운드 작업 (네트워크 요청, 파일 읽기/쓰기) | I/O 바운드 작업 (네트워크 요청, 파일 읽기/쓰기) | CPU 집약적인 작업 (데이터 분석, 이미지 처리, 계산 작업) |
처리 방식 | 이벤트 루프 기반 비동기 실행 | 스레드 기반 동시 실행 | 멀티프로세싱을 통한 프로세스 간 병렬 실행 |
동시성 | 단일 스레드에서 비동기적으로 여러 작업을 처리 | 여러 스레드가 동시에 실행되며, 스레드마다 독립적인 실행 흐름을 가짐 | 여러 코어에서 각각 독립된 프로세스를 병렬로 실행 |
GIL 문제 | GIL과 무관, 단일 스레드에서 비동기 작업 가능 | I/O 작업에서는 GIL 문제가 거의 없음, CPU 집약 작업에서는 성능 제한이 있음 | GIL과 무관, 각 프로세스가 독립적으로 실행됨 |
메모리 사용량 | 낮음 (단일 스레드 사용) | 스레드 간 메모리 공유 가능, 비교적 낮음 | 프로세스 간 메모리 분리, 높은 메모리 사용량 가능 |
오버헤드 | 스레드 오버헤드 없음, 이벤트 루프 관리 오버헤드 적음 | 스레드 생성과 컨텍스트 스위칭에 따른 오버헤드 존재 | 프로세스 생성과 관리에 따른 오버헤드가 큼 |
복잡성 | 비동기 방식이므로 스레드 동기화 문제 없음 | 스레드 간 동기화 필요, 데드락, 레이스 컨디션 발생 가능 | 프로세스 간 통신 필요 (IPC), 복잡한 데이터 공유 |
주요 Python 모듈 | asyncio , aiohttp |
threading |
multiprocessing , concurrent.futures |
사용 예 | - 네트워크 요청 처리 - 비동기 파일 읽기/쓰기 | - 파일 다운로드 - 로그 파일 모니터링 | - 데이터 분석 병렬 계산 - 이미지 처리 |
'그때그때 CS 정리' 카테고리의 다른 글
json 평활화 작업을 통한, 멀티스레드 속도 비교(ThreadPoolExecutor) (0) | 2024.11.03 |
---|---|
코드에서 배치 처리가 효율성을 가져오는 이유 (0) | 2024.09.05 |
GPU 작동원리 (2) | 2024.09.03 |
CPU와 GPU의 차이점 (1) | 2024.09.03 |
GPU 는 뭘까? (1) | 2024.09.03 |