目次
1. 들어가며
Python은 다양한 용도로 사용되는 프로그래밍 언어로, 데이터 처리, 머신러닝, 웹 개발 분야에서 특히 강력한 도구를 제공합니다. 그중에서도 multiprocessing 모듈은 병렬 처리를 구현하기 위한 중요한 라이브러리입니다. 이 글에서는 Python의 multiprocessing 모듈의 기본적인 사용법부터 응용까지 시각 자료를 곁들여 자세히 설명하고, 성능을 최대한 끌어내기 위한 실전적인 기법을 소개합니다。2. multiprocessing란?
2.1 병렬 처리의 필요성
Python은 기본적으로 싱글 스레드로 동작하지만, 무거운 처리나 대량의 데이터를 다루는 경우 이 방식만으로는 처리 속도에 한계가 있습니다. 병렬 처리를 활용하면 여러 작업을 동시에 실행하여 CPU의 모든 코어를 효율적으로 활용하고 처리 시간을 단축할 수 있습니다。multiprocessing 모듈은 Python의 GIL(Global Interpreter Lock)을 우회하여 여러 프로세스를 사용해 진정한 병렬 처리를 가능하게 합니다。2.2 싱글 스레드와의 차이
싱글 스레드에서는 하나의 프로세스가 작업을 순차적으로 실행하지만、멀티프로세스에서는 여러 프로세스가 동시에 작업을 처리합니다。이에 따라 특히 CPU 바운드한 작업(예: 대규모 수치 계산이나 데이터 분석)에서 성능이 향상됩니다。3. multiprocessing 모듈의 기본 문법
3.1 Process 클래스의 사용법
multiprocessing
모듈의 기본은 Process 클래스를 사용하는 것입니다. 이 클래스를 사용하면 새 프로세스를 쉽게 생성하고, 병렬 처리를 수행할 수 있습니다。import multiprocessing
def worker_function():
print("새로운 프로세스가 실행되었습니다")
if __name__ == "__main__":
process = multiprocessing.Process(target=worker_function)
process.start()
process.join()
이 코드에서는, worker_function
을 새 프로세스로 실행합니다. start()
메서드로 프로세스를 시작하고, join()
메서드로 프로세스의 종료를 기다릴 수 있습니다。3.2 프로세스에 인수를 전달하는 방법
프로세스에 인수를 전달하려면,args
매개변수를 사용합니다. 다음 예제에서는, worker
함수에 인수를 전달하여 동작시킵니다。def worker(number):
print(f'Worker {number}가 실행되었습니다')
if __name__ == "__main__":
process = multiprocessing.Process(target=worker, args=(5,))
process.start()
process.join()
이를 통해 프로세스에 동적인 데이터를 전달하여 처리를 병렬로 수행할 수 있습니다。4. 데이터 공유와 동기화
4.1 공유 메모리를 사용한 데이터 공유
멀티프로세스에서는 프로세스 간에 데이터를 안전하게 공유하기 위해 Value와 Array를 사용합니다. 이들은 공유 메모리 객체로서, 여러 프로세스가 동시에 접근하더라도 데이터를 안전하게 조작할 수 있도록 해줍니다.import multiprocessing
def increment_value(shared_value):
with shared_value.get_lock():
shared_value.value += 1
if __name__ == "__main__":
shared_value = multiprocessing.Value('i', 0)
processes = [multiprocessing.Process(target=increment_value, args=(shared_value,)) for _ in range(5)]
for process in processes:
process.start()
for process in processes:
process.join()
print(f'최종 값: {shared_value.value}')
위의 코드는 5개의 프로세스가 동시에 공유 메모리 내의 정수 값을 증가시키는 예입니다。get_lock()
을 사용하여 데이터 경합을 방지하고 있습니다。4.2 잠금으로 데이터 경합을 방지
여러 프로세스가 동시에 데이터를 조작할 때는 잠금 메커니즘을 사용하여 데이터 경합을 방지합니다.Lock
객체를 사용하면 프로세스 간 동기화가 보장됩니다。
5. 프로세스 풀을 사용한 작업 분배
5.1 Pool 클래스의 활용
Pool
클래스를 사용하면 여러 작업을 분할하여 여러 프로세스에서 병렬로 실행할 수 있습니다. 대량 데이터 처리나 작업 분배가 필요한 경우 매우 유용합니다.from multiprocessing import Pool
def square(x):
return x * x
if __name__ == "__main__":
with Pool(4) as pool:
results = pool.map(square, range(10))
print(results)
이 코드에서는 리스트의 요소에 대해 제곱을 계산하고 이를 4개의 프로세스에 분배하여 병렬로 실행합니다. map()
함수를 사용하면 작업을 프로세스에 손쉽게 분산시킬 수 있습니다。도해: Pool 클래스를 이용한 작업 분배
5.2 응용 예: starmap을 사용한 여러 인수 처리
starmap()
을 사용하면 여러 인수를 받는 함수를 병렬로 처리할 수 있습니다. 예를 들어 다음과 같이 인수 쌍에 기반해 처리를 수행할 수 있습니다。def multiply(x, y):
return x * y
if __name__ == "__main__":
with Pool(4) as pool:
results = pool.starmap(multiply, [(1, 2), (3, 4), (5, 6), (7, 8)])
print(results)
6. 최적의 CPU 리소스 활용
6.1 cpu_count()
로 프로세스 수 최적화
Python의multiprocessing.cpu_count()
를 사용하면 시스템의 물리 코어 수를 자동으로 가져와 이에 맞춰 프로세스 수를 설정할 수 있습니다. 이를 통해 과도한 프로세스 생성을 방지하고 시스템 전반의 성능을 최적화할 수 있습니다。from multiprocessing import Pool, cpu_count
if __name__ == "__main__":
with Pool(cpu_count() - 1) as pool:
results = pool.map(square, range(100))
print(results)
6.2 시스템 리소스의 효율적 활용
모든 CPU 코어를 사용하는 것은 피하고, 하나의 코어는 시스템 용도로 남겨 두면 다른 작업에 영향을 주지 않고 처리를 병렬화할 수 있습니다。</final7. 실제 사용 사례와 모범 사례
7.1 사용 사례의 구체적인 예
multiprocessing은 다음과 같은 상황에서 유용합니다。- 대규모 데이터 처리: 여러 파일을 동시에 읽고 처리할 때 유용합니다。
- 기계 학습의 병렬 훈련: 모델 훈련을 여러 프로세스로 동시에 실행하여 시간을 단축합니다。
- 웹 크롤링: 여러 페이지를 병렬로 크롤링하여 효율적으로 데이터를 수집할 수 있습니다。
7.2 모범 사례
- 리소스의 최적 배분: 시스템의 물리 코어 수에 맞춰 적절한 프로세스 수를 설정합니다。
- 디버깅과 로깅의 활용:
logging
모듈을 사용하여 각 프로세스의 상태를 추적하고, 오류 처리를 적절히 수행합니다。
import logging
import multiprocessing
def worker_function():
logging.info(f'프로세스 {multiprocessing.current_process().name}가 시작되었습니다')
if __name__ == "__main__":
logging.basicConfig(level=logging.INFO)
process = multiprocessing.Process(target=worker_function, name='워커1')
process.start()
process.join()
이 코드에서는 logging
을 사용하여 프로세스별 동작을 기록하고, 나중에 로그를 확인할 수 있도록 합니다.- 오류 처리의 구현: multiprocessing에서는 여러 프로세스가 동시에 실행되므로 오류 처리가 중요합니다. 프로세스가 비정상 종료하더라도 메인 프로세스나 다른 프로세스에 영향을 주지 않도록, try-except 구문을 사용해 오류 처리를 구현합니다。
