programing tip

다중 처리 프로세스간에 대규모 읽기 전용 Numpy 배열 공유

itbloger 2020. 10. 15. 07:35
반응형

다중 처리 프로세스간에 대규모 읽기 전용 Numpy 배열 공유


60GB SciPy Array (Matrix)가 있는데 5 multiprocessing Process이상의 개체 간에 공유해야 합니다. numpy-sharedmem을 보았고 SciPy 목록 에서이 토론읽었습니다 . 두 가지 접근 방식이있는 것 같습니다. numpy-sharedmema를 사용하고 multiprocessing.RawArray()NumPy를 dtypes에 매핑 ctype합니다. 이제 numpy-sharedmem갈 길인 것 같지만 아직 좋은 참조 예제를 보지 못했습니다. 배열 (실제로는 행렬)이 읽기 전용이기 때문에 어떤 종류의 잠금도 필요하지 않습니다. 이제 크기 때문에 사본을 피하고 싶습니다. 그것은 같은데 정확한 방법은 생성하는 A와 배열의 카피 sharedmem배열하고 그것이 패스 Process개체? 몇 가지 구체적인 질문 :

  1. 실제로 sharedmem 핸들을 서브에 전달하는 가장 좋은 방법은 무엇입니까 Process()? 하나의 배열을 전달하기 위해 대기열이 필요합니까? 파이프가 더 좋을까요? Process()서브 클래스의 init (피클이라고 가정하는 곳)에 인수로 전달할 수 있습니까 ?

  2. 위에서 링크 한 토론 numpy-sharedmem에서 64 비트 안전하지 않다는 언급이 있습니까? 32 비트 주소 지정이 불가능한 구조를 확실히 사용하고 있습니다.

  3. RawArray()접근 방식에 트레이드 오프가 있습니까? 더 느리게, 벌레?

  4. numpy-sharedmem 메서드에 대해 ctype-to-dtype 매핑이 필요합니까?

  5. 누구든지 이것을 수행하는 일부 OpenSource 코드의 예가 있습니까? 나는 매우 실습을 배웠고 어떤 종류의 좋은 예 없이는 이것을 작동시키기가 어렵습니다.

다른 사람들을 위해 이것을 명확히하기 위해 제가 제공 할 수있는 추가 정보가있는 경우, 의견을 남겨 주시면 추가하겠습니다. 감사!

이것은 Ubuntu Linux 및 Maybe Mac OS에서 실행되어야하지만 이식성은 큰 문제가 아닙니다.


@Velimir Mlaker가 훌륭한 대답을했습니다. 나는 약간의 코멘트와 작은 예를 추가 할 수 있다고 생각했다.

(sharedmem에 대한 많은 문서를 찾을 수 없었습니다. 이것은 내 실험의 결과입니다.)

  1. 하위 프로세스가 시작될 때 또는 시작된 후에 핸들을 전달해야합니까? 그냥 이전의 경우에, 당신은 바로 사용할 수 있습니다 targetargs위해 인수를 Process. 이것은 잠재적으로 전역 변수를 사용하는 것보다 낫습니다.
  2. 링크 한 토론 페이지에서 64 비트 Linux에 대한 지원이 한동안 sharedmem에 추가 된 것으로 보이므로 문제가되지 않을 수 있습니다.
  3. 나는 이것에 대해 모른다.
  4. 아니요. 아래 예를 참조하십시오.

#!/usr/bin/env python
from multiprocessing import Process
import sharedmem
import numpy

def do_work(data, start):
    data[start] = 0;

def split_work(num):
    n = 20
    width  = n/num
    shared = sharedmem.empty(n)
    shared[:] = numpy.random.rand(1, n)[0]
    print "values are %s" % shared

    processes = [Process(target=do_work, args=(shared, i*width)) for i in xrange(num)]
    for p in processes:
        p.start()
    for p in processes:
        p.join()

    print "values are %s" % shared
    print "type is %s" % type(shared[0])

if __name__ == '__main__':
    split_work(4)

산출

values are [ 0.81397784  0.59667692  0.10761908  0.6736734   0.46349645  0.98340718
  0.44056863  0.10701816  0.67167752  0.29158274  0.22242552  0.14273156
  0.34912309  0.43812636  0.58484507  0.81697513  0.57758441  0.4284959
  0.7292129   0.06063283]
values are [ 0.          0.59667692  0.10761908  0.6736734   0.46349645  0.
  0.44056863  0.10701816  0.67167752  0.29158274  0.          0.14273156
  0.34912309  0.43812636  0.58484507  0.          0.57758441  0.4284959
  0.7292129   0.06063283]
type is <type 'numpy.float64'>

관련 질문 이 유용 할 수 있습니다.


Linux (또는 POSIX 호환 시스템)를 사용하는 경우이 배열을 전역 변수로 정의 할 수 있습니다. 새 자식 프로세스를 시작할 때 Linux에서 multiprocessing사용 중 fork()입니다. 새로 생성 된 자식 프로세스는 메모리를 변경하지 않는 한 자동으로 부모와 메모리를 공유합니다 (기록 중 복사 메커니즘).

"나는 어떤 종류의 잠금도 필요하지 않습니다. 배열 (실제로는 행렬)이 읽기 전용이기 때문에"이 동작을 활용하는 것은 매우 간단하면서도 매우 효율적인 접근 방식입니다. 모든 하위 프로세스가 액세스합니다. 이 큰 numpy 배열을 읽을 때 실제 메모리의 동일한 데이터.

배열을 Process()생성자 에게 넘기지 마십시오 . 이것은 데이터를 자식에게 지시 multiprocessingpickle것입니다. 이는 귀하의 경우에 극도로 비효율적이거나 불가능할 것입니다. Linux fork()에서 자식 바로 뒤에는 동일한 물리적 메모리를 사용하는 부모의 정확한 복사본이므로 .NET에 전달한 target함수 내에서 행렬을 '포함하는'Python 변수에 액세스 할 수 있는지 확인하기 만하면 됩니다 Process(). 이것은 일반적으로 '전역'변수로 달성 할 수 있습니다.

예제 코드 :

from multiprocessing import Process
from numpy import random


global_array = random.random(10**4)


def child():
    print sum(global_array)


def main():
    processes = [Process(target=child) for _ in xrange(10)]
    for p in processes:
        p.start()
    for p in processes:
        p.join()


if __name__ == "__main__":
    main()

On Windows -- which does not support fork() -- multiprocessing is using the win32 API call CreateProcess. It creates an entirely new process from any given executable. That's why on Windows one is required to pickle data to the child if one needs data that has been created during runtime of the parent.


You may be interested in a tiny piece of code I wrote: github.com/vmlaker/benchmark-sharedmem

The only file of interest is main.py. It's a benchmark of numpy-sharedmem -- the code simply passes arrays (either numpy or sharedmem) to spawned processes, via Pipe. The workers just call sum() on the data. I was only interested in comparing the data communication times between the two implementations.

I also wrote another, more complex code: github.com/vmlaker/sherlock.

Here I use the numpy-sharedmem module for real-time image processing with OpenCV -- the images are NumPy arrays, as per OpenCV's newer cv2 API. The images, actually references thereof, are shared between processes via the dictionary object created from multiprocessing.Manager (as opposed to using Queue or Pipe.) I'm getting great performance improvements when compared with using plain NumPy arrays.

Pipe vs. Queue:

In my experience, IPC with Pipe is faster than Queue. And that makes sense, since Queue adds locking to make it safe for multiple producers/consumers. Pipe doesn't. But if you only have two processes talking back-and-forth, it's safe to use Pipe, or, as the docs read:

... there is no risk of corruption from processes using different ends of the pipe at the same time.

sharedmem safety:

The main issue with sharedmem module is the possibility of memory leak upon ungraceful program exit. This is described in a lengthy discussion here. Although on Apr 10, 2011 Sturla mentions a fix to memory leak, I have still experienced leaks since then, using both repos, Sturla Molden's own on GitHub (github.com/sturlamolden/sharedmem-numpy) and Chris Lee-Messer's on Bitbucket (bitbucket.org/cleemesser/numpy-sharedmem).


If your array is that big you can use numpy.memmap. For example, if you have an array stored in disk, say 'test.array', you can use simultaneous processes to access the data in it even in "writing" mode, but your case is simpler since you only need "reading" mode.

Creating the array:

a = np.memmap('test.array', dtype='float32', mode='w+', shape=(100000,1000))

You can then fill this array in the same way you do with an ordinary array. For example:

a[:10,:100]=1.
a[10:,100:]=2.

The data is stored into disk when you delete the variable a.

Later on you can use multiple processes that will access the data in test.array:

# read-only mode
b = np.memmap('test.array', dtype='float32', mode='r', shape=(100000,1000))

# read and writing mode
c = np.memmap('test.array', dtype='float32', mode='r+', shape=(100000,1000))

Related answers:


You may also find it useful to take a look at the documentation for pyro as if you can partition your task appropriately you could use it to execute different sections on different machines as well as on different cores in the same machine.

참고URL : https://stackoverflow.com/questions/17785275/share-large-read-only-numpy-array-between-multiprocessing-processes

반응형