programing

멀티프로세싱: 프로세스 간에 큰 읽기 전용 객체를 공유하시겠습니까?

powerit 2023. 7. 22. 10:31
반응형

멀티프로세싱: 프로세스 간에 큰 읽기 전용 객체를 공유하시겠습니까?

멀티프로세싱을 통해 생성된 하위 프로세스는 프로그램 초기에 생성된 개체를 공유합니까?

다음과 같은 설정이 있습니다.

do_some_processing(filename):
    for line in file(filename):
        if line.split(',')[0] in big_lookup_object:
            # something here

if __name__ == '__main__':
    big_lookup_object = marshal.load('file.bin')
    pool = Pool(processes=4)
    print pool.map(do_some_processing, glob.glob('*.data'))

저는 어떤 큰 물체를 메모리에 로드하고, 그 큰 물체를 활용해야 하는 작업자들의 풀을 만들고 있습니다.큰 개체는 읽기 전용으로 액세스되므로 프로세스 간에 수정 사항을 전달할 필요가 없습니다.

제 질문은 unix/c에서 프로세스를 생성한 경우와 마찬가지로 큰 개체가 공유 메모리에 로드됩니까? 아니면 각 프로세스가 큰 개체의 자체 복사본을 로드합니까?

업데이트: 자세히 설명 - big_lookup_object는 공유 룩업 개체입니다.저는 그것을 나눠서 따로 처리할 필요가 없습니다.저는 그것을 한 부 보관해야 합니다.분할해야 하는 작업은 다른 큰 파일을 많이 읽고 검색 개체를 기준으로 큰 파일의 항목을 검색하는 것입니다.

추가 업데이트: 데이터베이스는 훌륭한 솔루션이며 memcached가 더 나은 솔루션일 수 있으며 Disk의 파일(쉘프 또는 dbm)이 훨씬 더 나을 수 있습니다.이 질문에서 저는 특히 메모리 솔루션에 관심이 있었습니다.최종 솔루션으로 하둡을 사용할 예정입니다. 하지만 로컬 인메모리 버전도 사용할 수 있는지 알고 싶었습니다.

멀티프로세싱을 통해 생성된 하위 프로세스는 프로그램 초기에 생성된 개체를 공유합니까?

Python < 3.8의 경우 아니오, Python » 3.8의 경우 예.

프로세스에는 독립적인 메모리 공간이 있습니다.

솔루션 1

작업자가 많은 대형 구조물을 최대한 활용하려면 다음 작업을 수행합니다.

  1. – 를 "필터"로 읽습니다.stdin있고, 를 동작, 중결기에 씁니다.stdout.

  2. 모든 작업자를 파이프라인으로 연결:

    process1 <source | process2 | process3 | ... | processn >result
    

각 프로세스는 읽기, 작업 및 쓰기를 수행합니다.

이는 모든 프로세스가 동시에 실행되기 때문에 매우 효율적입니다.쓰기 및 읽기는 프로세스 간의 공유 버퍼를 직접 통과합니다.


솔루션 2

경우에 따라 더 복잡한 구조, 즉 팬아웃 구조를 가질 수 있습니다.이 경우 자녀가 여러 명인 부모가 있습니다.

  1. 상위 항목이 원본 데이터를 엽니다.부모님은 많은 아이들을 낳습니다.

  2. 부모는 소스를 읽고 소스의 일부를 동시에 실행 중인 각 자식에게 팜합니다.

  3. 상위 항목이 끝에 도달하면 파이프를 닫습니다.자식이 파일 끝을 받고 정상적으로 종료됩니다.

아이들은 각각 읽기만 하면 되기 때문에 아이들은 쓰기에 즐겁습니다.sys.stdin.

부모님은 모든 아이들에게 알을 낳고 파이프를 제대로 고정시키는 데 약간의 화려한 발놀림을 가지고 있지만, 그리 나쁘지는 않습니다.

팬인은 반대의 구조입니다.독립적으로 실행되는 여러 프로세스는 공통 프로세스에 입력을 삽입해야 합니다.수집기는 많은 출처에서 읽어야 하기 때문에 쓰기가 쉽지 않습니다.

은명명파읽것는종수사행다여됩니용하다음을종은많서이에를 됩니다.select모듈: 보류 중인 입력이 있는 파이프를 확인합니다.


솔루션 3

공유 검색은 데이터베이스의 정의입니다.

솔루션 3A – 데이터베이스를 로드합니다.작업자가 데이터베이스의 데이터를 처리하도록 합니다.

솔루션 3B – werkzeug(또는 유사한)를 사용하여 매우 간단한 서버를 생성하여 작업자가 서버를 쿼리할 수 있도록 HTTP GET에 응답하는 WSGI 애플리케이션을 제공합니다.


솔루션 4

공유 파일 시스템 개체입니다.Unix OS는 공유 메모리 개체를 제공합니다.이러한 파일은 메모리에 매핑되어 있으므로 더 많은 기존 버퍼링된 읽기 대신 I/O를 스왑할 수 있습니다.

여러 가지 방법으로 Python 컨텍스트에서 이 작업을 수행할 수 있습니다.

  1. (1) 당신의 원래 거대한 물체를 더 작은 물체로 나누고, (2) 각각 더 작은 물체로 작업자를 시작하는 스타트업 프로그램을 작성하세요.작은 개체는 Python 개체를 피클링하여 파일 읽기 시간을 조금 절약할 수 있습니다.

  2. (1) ▁(-▁▁using▁write▁and▁startup▁that▁object▁gigantic다▁a▁page▁(▁writes)▁byte▁a▁original작니▁your)11▁file합coded성ured를 이용하여 페이지 구조화된 바이트코드화된 파일을 작성하는 스타트업 프로그램을 작성합니다.seek간단한 검색으로 개별 섹션을 쉽게 찾을 수 있도록 보장하는 작업.이 데이터베이스 이 하는입니다. 여러 , 이이데엔하일다입을 각 찾을 수 있도록 . 데이터를 페이지로 나누고 각 페이지를 쉽게 찾을 수 있도록 합니다.seek.

이 큰 페이지 구조 파일에 대한 액세스 권한을 가진 작업자를 생성합니다.각 근로자는 관련 부분을 찾아 그곳에서 업무를 수행할 수 있습니다.

멀티프로세싱을 통해 생성된 하위 프로세스는 프로그램 초기에 생성된 개체를 공유합니까?

사정에 따라 다르겠지.글로벌 읽기 전용 변수의 경우 사용된 메모리를 제외하고는 그렇지 않은 것으로 간주할 수 있습니다.

멀티프로세싱의 설명서에는 다음과 같이 나와 있습니다.

Better to inherit than pickle/unpickle

Windows에서는 하위 프로세스에서 사용할 수 있도록 다중 처리의 여러 유형을 선택할 수 있어야 합니다.그러나 일반적으로 파이프나 대기열을 사용하여 공유 개체를 다른 프로세스로 보내는 것은 피해야 합니다.대신 다른 곳에서 생성된 공유 리소스에 대한 액세스가 필요한 프로세스가 상위 프로세스에서 이를 상속할 수 있도록 프로그램을 정렬해야 합니다.

Explicitly pass resources to child processes

유닉스에서 하위 프로세스는 글로벌 리소스를 사용하여 상위 프로세스에서 생성된 공유 리소스를 사용할 수 있습니다.그러나 하위 프로세스에 대해 생성자에게 개체를 인수로 전달하는 것이 좋습니다.

코드를 (잠재적으로) Windows와 호환되도록 만드는 것 외에도 하위 프로세스가 아직 활성화되어 있는 한 개체가 상위 프로세스에서 가비지 수집되지 않도록 합니다.이는 개체가 상위 프로세스에서 가비지 수집될 때 일부 리소스가 해제된 경우에 중요할 수 있습니다.

Global variables

하위 프로세스에서 실행되는 코드가 전역 변수에 액세스하려고 하면 해당 변수에 표시되는 값(있는 경우)이 Process.start()가 호출된 시점의 상위 프로세스의 값과 다를 수 있습니다.

Windows(단일 CPU)의 경우:

#!/usr/bin/env python
import os, sys, time
from multiprocessing import Pool

x = 23000 # replace `23` due to small integers share representation
z = []    # integers are immutable, let's try mutable object

def printx(y):
    global x
    if y == 3:
       x = -x
    z.append(y)
    print os.getpid(), x, id(x), z, id(z) 
    print y
    if len(sys.argv) == 2 and sys.argv[1] == "sleep":
       time.sleep(.1) # should make more apparant the effect

if __name__ == '__main__':
    pool = Pool(processes=4)
    pool.map(printx, (1,2,3,4))

와 함께sleep:

$ python26 test_share.py sleep
2504 23000 11639492 [1] 10774408
1
2564 23000 11639492 [2] 10774408
2
2504 -23000 11639384 [1, 3] 10774408
3
4084 23000 11639492 [4] 10774408
4

없이.sleep:

$ python26 test_share.py
1148 23000 11639492 [1] 10774408
1
1148 23000 11639492 [1, 2] 10774408
2
1148 -23000 11639324 [1, 2, 3] 10774408
3
1148 -23000 11639324 [1, 2, 3, 4] 10774408
4

S.롯트가 맞습니다.Python의 다중 처리 바로 가기는 효과적으로 별도의 중복된 메모리 청크를 제공합니다.

대부분의 *nix 시스템에서 하위 수준 호출을 사용하여os.fork()사실, 쓰기 시 복사 메모리를 제공할 것이고, 그것이 당신이 생각하고 있는 것일 수도 있습니다.AFAIK는 이론적으로, 가능한 한 가장 단순한 프로그램으로, 데이터를 중복하지 않고 읽을 수 있습니다.

하지만 파이썬 인터프리터에서는 상황이 그렇게 간단하지 않습니다.개체 데이터와 메타데이터는 동일한 메모리 세그먼트에 저장되므로 개체가 변경되지 않더라도 해당 개체에 대한 참조 카운터와 같은 것이 증분되면 메모리 쓰기가 발생하므로 복사본이 생성됩니다.인쇄 '안녕하세요' 이상을 수행하는 거의 모든 Python 프로그램은 참조 카운트 증가를 유발하므로 쓰기 시 복사의 이점을 결코 깨닫지 못할 것입니다.

Python에서 공유 메모리 솔루션을 해킹하는 데 성공했다고 하더라도 프로세스 간에 가비지 수집을 조정하는 것은 매우 고통스러울 것입니다.

유닉스에서 실행 중인 경우 포크가 작동하는 방식(즉, 자식 프로세스는 별도의 메모리를 가지고 있지만 쓰기 시 복사이므로 아무도 수정하지 않는 한 공유될 수 있음)으로 인해 동일한 개체를 공유할 수 있습니다.다음을 시도했습니다.

import multiprocessing

x = 23

def printx(y):
    print x, id(x)
    print y

if __name__ == '__main__':
    pool = multiprocessing.Pool(processes=4)
    pool.map(printx, (1,2,3,4))

다음과 같은 출력을 얻었습니다.

./mtest.파이의23 22995656123 22995656223 22995656323 229956564

물론 이것이 복사본이 만들어지지 않았다는 것을 증명하는 것은 아니지만, 당신의 상황에서 그것을 확인할 수 있어야 합니다.ps각 하위 프로세스에서 사용 중인 실제 메모리 양을 확인합니다.

프로세스마다 주소 공간이 다릅니다.인터프리터의 다른 인스턴스를 실행하는 것과 같습니다.그것이 IPC(Interprocess communication)의 목적입니다.

이 용도로 큐 또는 파이프를 사용할 수 있습니다.나중에 네트워크를 통해 프로세스를 배포하려는 경우에도 tcp를 통해 rpc를 사용할 수 있습니다.

http://docs.python.org/dev/library/multiprocessing.html#exchanging-objects-between-processes

멀티프로세싱 그 자체와는 직접적인 관련이 없지만, 당신의 예를 들어보면 선반 모듈 같은 것을 사용할 수 있을 것 같습니다."big_lookup_object"가 정말 메모리에 완전히 있어야 합니까?

아니요, 그러나 하위 프로세스로 데이터를 로드하고 다른 하위 프로세스와 데이터를 공유하도록 허용할 수 있습니다.아래를 참조하십시오.

import time
import multiprocessing

def load_data( queue_load, n_processes )

    ... load data here into some_variable

    """
    Store multiple copies of the data into
    the data queue. There needs to be enough
    copies available for each process to access. 
    """

    for i in range(n_processes):
        queue_load.put(some_variable)


def work_with_data( queue_data, queue_load ):

    # Wait for load_data() to complete
    while queue_load.empty():
        time.sleep(1)

    some_variable = queue_load.get()

    """
    ! Tuples can also be used here
    if you have multiple data files
    you wish to keep seperate.  
    a,b = queue_load.get()
    """

    ... do some stuff, resulting in new_data

    # store it in the queue
    queue_data.put(new_data)


def start_multiprocess():

    n_processes = 5

    processes = []
    stored_data = []

    # Create two Queues
    queue_load = multiprocessing.Queue()
    queue_data = multiprocessing.Queue()

    for i in range(n_processes):

        if i == 0:
            # Your big data file will be loaded here...
            p = multiprocessing.Process(target = load_data,
            args=(queue_load, n_processes))

            processes.append(p)
            p.start()   

        # ... and then it will be used here with each process
        p = multiprocessing.Process(target = work_with_data,
        args=(queue_data, queue_load))

        processes.append(p)
        p.start()

    for i in range(n_processes)
        new_data = queue_data.get()
        stored_data.append(new_data)    

    for p in processes:
        p.join()
    print(processes)    

Linux/Unix/MacOS 플랫폼의 경우 Forkmap은 빠르고 더러운 솔루션입니다.

언급URL : https://stackoverflow.com/questions/659865/multiprocessing-sharing-a-large-read-only-object-between-processes

반응형