본문 바로가기
Artificial Intelligence/60. Python

[PYTHON] Shared Memory를 활용한 3가지 데이터 통신 최적화 방법과 성능 차이 해결

by Papa Martino V 2026. 3. 17.
728x90

Shared Memory
Shared Memory

 

파이썬의 멀티프로세싱 환경에서 가장 큰 성능 병목 중 하나는 '프로세스 간 데이터 통신(IPC)'입니다. 기본적으로 파이썬의 프로세스는 독립된 메모리 공간을 가지므로, 데이터를 주고받을 때 객체를 직렬화(Serialization)하고 다시 역직렬화하는 오버헤드가 발생합니다. 하지만 Shared Memory(공유 메모리)를 활용하면 복사본을 만들지 않고도 동일한 메모리 블록에 직접 접근하여 이 문제를 근본적으로 해결할 수 있습니다. 본 글에서는 Python 3.8부터 도입된 multiprocessing.shared_memory를 중심으로 최적화 전략을 다룹니다.


1. IPC 방식에 따른 메커니즘과 성능 차이 비교

파이썬에서 데이터를 교환하는 방식은 다양합니다. 각 방식이 메모리 레이아웃과 CPU 자원을 어떻게 소모하는지 비교하는 것이 최적화의 첫걸음입니다.

통신 방식 데이터 복사 여부 주요 메커니즘 데이터 전송 성능 차이
Queue / Pipe 예 (Pickle 활용) 객체를 바이트로 직렬화하여 전달 느림 (오버헤드 높음)
Manager 객체 예 (Proxy 활용) 서버 프로세스를 통한 간접 접근 매우 느림 (네트워크 유사 비용)
Shared Memory 아니오 (직접 접근) 운영체제 커널의 공유 메모리 블록 점유 매우 빠름 (최고 성능)
Value / Array 아니오 C 타입의 공유 데이터 구조 사용 빠름 (타입 제한적)

2. Shared Memory 최적화를 위한 3가지 핵심 전략

단순히 메모리를 공유하는 것만으로는 충분하지 않습니다. 동시성 제어와 자원 해제 문제를 해결해야 진정한 성능 최적화가 완성됩니다.

전략 01: NumPy와의 결합을 통한 대용량 행렬 처리

공유 메모리 블록을 NumPy 배열의 버퍼로 연결하면, 직렬화 없이 기가바이트 단위의 데이터를 여러 프로세스가 0.001초 만에 공유할 수 있습니다. 이는 이미지 처리나 AI 모델 추론 시 필수적인 해결 방법입니다.

전략 02: Race Condition 방지를 위한 Lock 최소화

공유 메모리는 여러 프로세스가 동시에 쓰기를 시도할 때 데이터 오염이 발생할 수 있습니다. 전체 메모리에 Lock을 거는 대신, 메모리 구역을 나누어 각 프로세스에 할당하거나 Atomic Operation을 유도하여 대기 시간을 줄여야 합니다.

전략 03: 리소스 누수(Resource Leak) 방지 해결

Shared Memory는 파이썬 프로세스가 종료되어도 OS 커널에 남아 있을 수 있습니다. 반드시 shm.close()shm.unlink()를 호출하여 메모리 파편화를 방지하는 자동화 로직을 구현해야 합니다.

3. 실전 샘플 예제 (Sample Example)

다음은 공유 메모리를 생성하고 NumPy 배열을 연결하여 두 프로세스가 데이터를 주고받는 최적화 코드 예제입니다.


import numpy as np
from multiprocessing import Process
from multiprocessing import shared_memory

def modify_data(shm_name, shape, dtype):
    # 1. 기존 공유 메모리에 연결
    existing_shm = shared_memory.SharedMemory(name=shm_name)
    # 2. 공유 메모리를 NumPy 배열로 래핑 (복사 발생 안 함)
    shared_array = np.ndarray(shape, dtype=dtype, buffer=existing_shm.buf)
    
    print(f"작업 프로세스: 데이터 수신 완료 (첫번째 값: {shared_array[0]})")
    shared_array[:] = shared_array * 2  # 모든 데이터 2배 증가
    print("작업 프로세스: 데이터 수정 완료")
    
    existing_shm.close()

if __name__ == "__main__":
    # 메인 데이터 생성 (100만 개의 부동 소수점)
    data = np.random.rand(1000000)
    
    # 3. 공유 메모리 블록 생성
    shm = shared_memory.SharedMemory(create=True, size=data.nbytes)
    
    # 4. 공유 메모리에 데이터 할당
    shared_np = np.ndarray(data.shape, dtype=data.dtype, buffer=shm.buf)
    shared_np[:] = data[:]
    
    p = Process(target=modify_data, args=(shm.name, data.shape, data.dtype))
    p.start()
    p.join()
    
    print(f"메인 프로세스: 수정된 데이터 확인 (첫번째 값: {shared_np[0]})")
    
    # 5. 리소스 정리 (매우 중요)
    shm.close()
    shm.unlink()

4. 결론: 왜 공유 메모리가 정답인가?

대규모 데이터 처리 시스템에서 Pickle을 사용하는 Queue 방식은 데이터 크기가 커질수록 직렬화 비용이 기하급수적으로 증가합니다. Shared Memory는 데이터의 소유권을 이전하는 것이 아니라 '공간'을 공유함으로써 이 비용을 '0'에 가깝게 만듭니다. 고성능 파이썬 애플리케이션을 지향한다면, 위에서 언급한 메모리 관리 규칙을 준수하며 공유 메모리를 적극 도입해 보시기 바랍니다.


내용 출처 및 참고 자료

  • Python 3.12 Documentation: "multiprocessing.shared_memory — Shared memory for direct access"
  • NumPy Documentation: "Interoperability with other libraries via Buffer Protocol"
  • High Performance Python (2025 Revised Edition) - IPC Optimization Section.
  • Linux Kernel Docs: "POSIX Shared Memory Mechanisms."
728x90