[multiprocessing] SharedMemory / Pipe / Queue / Manager / Event

About_work·2024년 2월 11일
0

process, thread

목록 보기
12/23

1. 각각 장단점/사용목적 비교

SharedMemory

사용 시나리오

  • 대량의 데이터(예: numpy 배열)를 프로세스 간에 공유할 때 사용

장점

  • 메모리를 직접 공유하기 때문에 대량의 데이터를 빠르게 전달할 수 있음.
  • 데이터 복사 과정이 없어, 다른 IPC 메커니즘에 비해 효율적

단점

  • 동시성 제어(comcurrency control)를 개발자가 직접 관리해야 함
    • 예를 들어, 뮤텍스(mutexes)나 세마포어(semaphores)를 사용해야 할 수도 있음
  • 데이터 구조가 고정되어야 하며, 동적인 데이터 구조에는 적합하지 않을 수 있음

지원 가능한 데이터 type

  • 바이트-호환 데이터 (bytes, bytearray 등)
  • array.array, numpy.array 같은 배열 데이터
  • C의 구조체를 모방한 ctypes 모듈의 데이터 구조

Pipe

사용 시나리오

  • 주로 두 프로세스 간의 단방향 또는 양방향 통신에 사용
  • 실시간 데이터 스트리밍이나 작은 메시지를 주고받을 때 유용

장점

  • 구현이 비교적 간단하며, 사용하기 쉬움
  • 데이터 스트림을 통한 실시간 통신에 적합

단점

  • 데이터가 순차적으로 전송되기 때문에, 대량의 데이터 전송에는 비효율적
  • 주로 두 프로세스 간에만 사용됩니다.
  • 여러 프로세스가 통신해야 하는 경우 다른 방법을 고려해야 합니다.

Queue

사용 시나리오

  • 여러 프로세스 간의 데이터를 안전하게 교환해야 할 때 사용
  • 작업 항목을 여러 프로세스에 분배하고 결과를 수집하는 등의 작업에 적합

장점

  • 사용이 쉽고, 프로세스 간 안전한 통신을 보장
    • 내부적으로 Locking 메커니즘을 사용하여 데이터의 일관성을 유지

단점

  • 큐를 통한 데이터 전송은 상대적으로 느릴 수 있으며,
  • 대량의 데이터 전송에는 비효율적

Manager

사용 시나리오

  • 프로세스 간에 Python 객체(예: 리스트, 딕셔너리, 자체 정의 클래스 등)를 공유해야 할 때 사용합니다.
  • 또한 원격 프로세스 간에 객체를 공유할 수도 있음

장점

  • 다양한 공유 객체 타입을 제공하여, 복잡한 데이터 구조의 공유가 가능
  • 내부적으로 동시성 제어를 처리하여, 사용자가 직접 관리할 필요가 없음

단점

  • Manager를 통한 데이터 접근은 네트워크를 통해 이루어지기 때문에, 다른 공유 메커니즘에 비해 느릴 수 있습니다.
  • 상대적으로 높은 오버헤드로 인해, 성능이 중요한 애플리케이션에서는 사용에 주의가 필요합니다.

지원 가능한 datatype

리스트(list)
딕셔너리(dict)
네임스페이스(Namespace)
록(Lock, RLock)
세마포어(Semaphore, BoundedSemaphore)
조건(Condition)
이벤트(Event)
큐(Queue)
값(Value)
배열(Array)
사용자 정의 클래스(매니저를 통해 프록시화하여 사용)

통신 방식 설명

  • Manager 객체를 사용하는 경우, Python의 multiprocessing 라이브러리는 내부적으로 두 가지 주요 통신 방식을 활용할 수 있습니다:
    • 네트워크를 통한 통신
    • 내부적인 메시지 전달 메커니즘(예: 파이프, 소켓).
네트워크를 통한 통신
  • 사용 상황:
    • 네트워크를 통한 통신은 주로 분산 시스템이나, 여러 컴퓨터 간의 프로세스 통신에 사용
    • 이 방법은 물리적으로 분리된 시스템 간에 데이터를 공유할 때 유용
  • 원리:
    • Manager 서버가 특정 호스트와 포트에서 실행되며, 클라이언트(다른 프로세스)는 네트워크를 통해 이 서버에 접속합니다.
    • 서버는 Python 객체와 데이터 구조를 관리하고, 클라이언트의 요청에 따라 해당 객체를 조작하거나 상태 정보를 반환합니다.
    • 이 과정에서 TCP/IP 프로토콜과 같은 네트워크 통신 프로토콜을 사용하여 데이터를 교환합니다.
내부적인 메시지 전달 메커니즘
  • 사용 상황:
    • 내부적인 메시지 전달 메커니즘은 같은 머신에서 실행되는 프로세스 간의 통신에 주로 사용
    • 이 방법은 프로세스 간의 직접적이고 빠른 데이터 교환을 필요로 할 때 적합
  • 원리:
    • Python의 multiprocessing 라이브러리는 파이프(pipe), 소켓(socket), 혹은 공유 메모리를 사용하여 프로세스 간에 메시지를 전달
    • 이러한 메커니즘은 운영체제의 기본 제공 기능을 활용하여 구현
    • 예를 들어, 파이프는 두 프로세스 간의 양방향 통신 채널을 제공하며, 데이터는 연속된 바이트 스트림으로 전송됩니다. 소켓을 사용하는 경우, 프로세스는 네트워크 인터페이스를 통하지 않고도 동일한 시스템 내에서 서로 통신할 수 있습니다.
구분 사용과 성능 고려사항

multiprocessing.Manager는 이러한 통신 메커니즘을 추상화하여, 개발자가 복잡한 통신 세부 사항을 직접 관리하지 않고도 프로세스 간에 고수준의 데이터 공유와 통신을 구현할 수 있도록 돕습니다.


Event

사용 시나리오

  • 한 프로세스가 다른 프로세스에 특정 사건의 발생을 알릴 때 사용
  • 예를 들어, 작업을 중지하거나 특정 조건이 충족되었음을 알릴 때 사용

장점

  • 간단한 신호 메커니즘을 통해 프로세스 간 동기화를 쉽게 할 수 있음
  • 사용이 간단하고, 리소스 사용이 매우 적음

단점

  • 복잡한 데이터를 전송하는 데는 적합하지 않으며, 주로 상태 신호나 플래그 용도로 사용

5. SharedMemory의 제한적 데이터 타입 지원 이유??

  • SharedMemory가 제한적인 데이터 타입만을 지원하는 반면, Manager는 거의 모든 Python 데이터 타입을 공유할 수 있다는 특징
  • 이러한 차이는 두 기술의 구현 방식과 사용 목적에 기인합니다.

5.1. SharedMemory의 제한적 데이터 타입 지원

  • SharedMemory: 여러 프로세스가 메모리의 동일한 물리적 영역을 공유할 수 있도록 하여 데이터를 교환
  • 이 기술은 주로 원시 데이터(raw data)를 고속으로 공유할 필요가 있을 때 사용
  • SharedMemory는 고성능을 목적으로 하기 때문에, 직접적인 메모리 접근을 통한 데이터 공유를 지원
  • 이 때문에 복잡한 Python 객체나 상태 정보를 내장한 객체는 직접 공유하기 어렵습니다.
  • SharedMemory를 통해 공유할 수 있는 데이터는 주로 바이트 배열, 정수, 실수 등 원시 데이터 타입에 국한됩니다.
  • 복잡한 객체를 공유하려면 해당 객체를 직렬화하여 바이트 배열로 변환한 뒤 공유 메모리에 저장해야 하며, 사용하는 측에서는 이를 다시 역직렬화하여 객체를 복원해야 합니다.

Manager의 광범위한 데이터 타입 지원

  • Manager 객체는 서버 프로세스를 통해 다양한 데이터 타입을 공유
  • 이 방식은 내부적으로 객체의 상태를 관리하고, 요청에 따라 해당 상태를 접근하거나 수정할 수 있는 인터페이스를 제공
  • Manager를 사용하면 리스트, 딕셔너리, 사용자 정의 객체 등 Python에서 사용하는 대부분의 데이터 타입을 안전하게 공유
  • 이는 Manager고수준의 추상화를 제공하고, 객체의 메서드 호출을 프로세스 간에 전달할 수 있도록 설계되었기 때문입니다.
  • Manager를 통한 데이터 공유는 네트워크를 통한 통신이나 내부적인 메시지 전달 메커니즘을 사용하기 때문에 SharedMemory에 비해 상대적으로 성능 오버헤드가 큽니다.

결론

  • SharedMemory의 데이터 타입 제한은:직접적인 메모리 접근을 통한 고성능 데이터 공유의 필요성에서 비롯
  • 반면, Manager의 광범위한 데이터 타입 지원: 고수준의 추상화와 복잡한 객체 관리를 가능하게 하는 서버-클라이언트 기반의 구현에서 기인
  • 선택은 사용 사례에 따라 달라지며, 고성능이 필요한 원시 데이터 공유에는 SharedMemory를, 복잡한 객체 공유와 편의성에는 Manager를 사용하는 것이 일반적입니다.

3. SharedMemory가 지원하지 않는 DATA를 어떻게 공유할까? IPC: SharedMemory VS Manager

  • 인스턴스를 프로세스 간에 공유하는 두 가지 방법,
    • SharedMemory를 이용한 직렬화/역직렬화 방식
    • Manager를 이용한 바로 사용 방식

3.1. SharedMemory를 이용한 직렬화/역직렬화 방식

장점:

  • 효율성: 큰 데이터를 공유할 때 SharedMemory 방식이 더 효율적
  • 저수준 제어: 개발자가 메모리 사용을 더 세밀하게 제어할 수 있습니다. 특정 데이터 구조를 매우 효율적으로 공유할 수 있는 방법을 제공

단점:

  • 복잡성: 직렬화와 역직렬화 과정이 필요하며, 공유 메모리 관리가 필요합니다. 이는 구현을 복잡하게 만들 수 있습니다.
  • 타입 제한: 모든 Python 객체가 직렬화 가능한 것은 아니므로, 일부 데이터 타입은 이 방법을 사용하기 어려울 수 있습니다.

3.2. Manager를 이용한 방식

장점:

  • 간편성: Manager를 사용하면 복잡한 직렬화/역직렬화 과정 없이 객체를 쉽게 공유할 수 있습니다. 코드가 간결하고 이해하기 쉽습니다.
  • 유연성: 다양한 데이터 타입을 지원하며, 사용자 정의 객체도 비교적 쉽게 공유할 수 있습니다.
  • 안정성: Python의 Manager는 프로세스 간의 안전한 통신을 위해 설계되었습니다. 데이터 무결성을 유지하기 쉽습니다.

단점:

  • 성능 오버헤드:
    • Manager를 통한 데이터 접근은 네트워크를 통한 통신처럼 처리되므로, SharedMemory 방식에 비해 성능이 떨어질 수 있습니다.
  • 자원 사용량:
    • 메모리와 CPU 사용량이 SharedMemory 방식보다 많을 수 있으며, 큰 데이터를 다룰 때 성능 저하가 발생할 수 있습니다.

3.3. 어떤 상황에서 어떤 방법을 추천하는가?

  • 큰 데이터셋을 공유하고 성능이 중요한 경우:
    • SharedMemory를 이용한 직렬화/역직렬화 방식이 더 적합할 수 있습니다.
    • 데이터 접근 속도가 중요하고, 개발자가 메모리 관리에 자신이 있을 때 추천
  • 간단한 데이터 공유, 높은 수준의 추상화, 또는 개발 용이성이 중요한 경우:
    • Manager를 사용하는 방식이 더 적합

3.4. 심화 설명

  • SharedMemory를 사용하여 복잡한 객체를 공유하기 위해 직렬화/역직렬화 기법을 활용하는 경우, 고려해야 할 성능 관련 측면이 있습니다.

  • 이 방법의 핵심은 객체를 바이트 스트림으로 변환하여 공유 메모리에 저장한 다음, 필요한 프로세스에서 이를 다시 객체로 변환하는 과정입니다.

  • 이 접근법이 SharedMemory의 주요 장점인 고성능을 저해하는지 여부는 여러 요인에 따라 달라질 수 있습니다.

  • 고성능 장점의 상대적 감소

    • 직렬화/역직렬화 오버헤드:
      • 직렬화와 역직렬화 과정은 추가적인 CPU 시간을 소모
      • 크기가 크거나 복잡한 객체의 경우, 이 과정이 상당한 오버헤드로 작용할 수 있음
      • 따라서, 빈번한 읽기/쓰기가 필요한 애플리케이션에서는 SharedMemory를 사용하더라도 직렬화/역직렬화로 인해 성능 이점이 줄어들 수 있습니다.
  • 성능 저하의 상황

    • 데이터의 크기와 복잡성:
      • 공유하려는 객체의 데이터 크기가 크고 복잡할수록
      • 직렬화와 역직렬화 과정에서의 성능 저하가 더욱 두드러질 수 있습니다.
    • 업데이트 빈도:
      • 객체가 자주 업데이트되고, 그때마다 전체 객체를 직렬화하고 역직렬화해야 한다면, 이 과정에서 발생하는 오버헤드가 전체 성능에 영향을 줄 수 있습니다.
  • 사용 상황에 따른 고려

    • SharedMemory를 이용한 직렬화/역직렬화 방식은 특정 상황에서 유리할 수 있습니다.
    • 예를 들어, 초기 설정 단계에서 한 번만 데이터를 로드하고, 이후에는 주로 읽기 작업이 이루어지는 경우,
    • 직렬화/역직렬화로 인한 초기 오버헤드가 크게 문제되지 않을 수 있습니다.
    • 반면, 데이터가 자주 변경되고 이러한 변경사항을 실시간으로 여러 프로세스와 공유해야 하는 상황에서는 Manager와 같은 다른 IPC 메커니즘을 고려하는 것이 더 적합할 수 있습니다.

4. 직렬화 가능/불가능한 python 객체

  • Python에서 직렬화는 객체를 바이트 스트림으로 변환하는 과정을 말하며, 주로 pickle 모듈을 통해 이루어집니다.
  • 직렬화는 데이터 저장, 전송 등에 사용되며, 직렬화 가능 여부는 객체의 복잡성, 내부 참조, 내장 타입 여부 등에 따라 달라집니다.

4.1. 직렬화 가능한 객체 종류

  • 기본 데이터 타입: 정수, 부동소수점 수, 문자열, 불리언
  • 컨테이너 타입: 리스트, 튜플, 딕셔너리, 세트 (내부에 직렬화 가능한 객체만 포함하는 경우)
  • 사용자 정의 클래스의 인스턴스: __dict__ 또는 __getstate__() / __setstate__() 메서드를 통해 직렬화 및 역직렬화를 정의할 수 있음
  • 함수, 클래스, 메서드: 모듈 수준의 함수와 클래스, 정적 메서드, 클래스 메서드 (하지만 클로저 내부의 함수나 람다, 인스턴스 메서드 등은 제한적)
  • 모듈: 모듈 객체 자체는 직렬화 가능하지만, 사용 시 주의가 필요함

4.2. 직렬화 불가능한 객체 종류

  • 파일, 소켓, 윈도우, 파일 객체 등의 I/O 객체: 이들은 운영체제 리소스를 관리하며, 상태가 복잡하고 프로세스에 종속적이므로 직접적인 직렬화가 불가능함
  • 클로저 내의 함수, 람다 함수: 외부 변수에 대한 참조를 포함하기 때문에 직렬화 과정에서 문제가 발생할 수 있음
  • 쓰레드, 프로세스: 실행 중인 쓰레드나 프로세스의 상태를 직렬화하는 것은 불가능함
  • 일부 제3자 라이브러리 객체: 내부 구현에 따라 직렬화가 지원되지 않을 수 있음

4.3. 사용 시 고려사항 (중요)

  • 직렬화 가능한 객체라 하더라도, 직렬화 과정에서 객체의 일부 속성이 유실되거나, 역직렬화 시 원본 객체와 정확히 동일한 상태로 복원되지 않을 수 있습니다.
    • 실제 함수나 메서드의 코드 자체를 저장하는 것이 아니라, 그것을 "찾을 수 있는 방법"을 저장
  • 예를 들어, 직렬화 시 함수나 메서드는 참조만 저장되므로, 역직렬화를 수행하는 환경에서 해당 함수나 메서드가 동일한 모듈 경로에 존재해야 합니다.
    • 그러므로, Python의 모듈 검색 경로(sys.path)에 그 모듈이 위치해 있어야 함을 의미
    • 따라서, 프로세스 A와 B가 서로 다른 환경에서 실행되는 경우(예를 들어, 다른 가상 환경이나, 서로 다른 서버에서 실행되는 경우), 프로세스 A에서 사용되는 모듈과 함수가 프로세스 B에서도 사용 가능해야 합니다.
  • 직렬화 및 역직렬화는 데이터의 저장, 전송을 위해 필수적인 과정일 수 있지만, 보안 측면에서 주의가 필요합니다.
  • pickle을 사용한 직렬화 데이터는 실행 가능한 코드를 포함할 수 있으므로, 신뢰할 수 없는 소스로부터 역직렬화를 수행할 때는 보안 문제가 발생할 수 있습니다.

profile
새로운 것이 들어오면 이미 있는 것과 충돌을 시도하라.

0개의 댓글