2016-08-08 2 views
5

필자는 필자의 함수의 인수로 반복되는 n 개의 요소 (아래의 "쌍"이라고 함)에 대해 작업합니다. 분명히 "r"목록이 모든 메모리를 소비하기에 충분히 크지 않은 한 모든 것이 잘 작동합니다. 문제는 결국 6 가지 요소에 대해 16 번 이상 반복해야한다는 것입니다. 나는 이것을 위해 구름 속에서 40 코어 시스템을 사용한다. 다중 처리 기능이있는 itertools - 거대한리스트 대 비효율적 인 CPU 사용량 반복기 사용시

내가 가진 문제를 해결하기 위해 노력 .. 내가 대신 선행 여기에 문제가 시작되는 거대한 목록을 생성하는 반복자를 사용한다고 생각

if __name__ == '__main__': 
    pool = Pool(39) 
    r = itertools.product(pairs,repeat=16) 
    pool.map(f, r) 

:

코드는 다음과 같습니다 보이는 다음 코드 :

if __name__ == '__main__': 
    pool = Pool(39) 
    for r in itertools.product(pairs,repeat=14): 
    pool.map(f, r) 

메모리 문제는 사라집니다하지만 CPU의 사용은 코어 당 5 %와 같다. 이제 코드의 싱글 코어 버전이 더 빠릅니다.

당신이 나에게 조금 안내 수 있다면 정말 감사하겠습니다 ..

감사합니다.

+0

사이드 노트 : 현대 Python (Python 3.3 이상)을 사용하는 경우'Pool'을'with' 문과 함께 사용하는 것이 가장 좋으므로'Pool' 작업자는 예상대로 정리됩니다. 단지'pool = Pool (39)'를'Pool (39) as pool :'으로 변경하고 풀을 사용하는 그 아래의 줄을 들여 쓰기하십시오. 블록이 종료되면 작업자는 즉시 정리됩니다. – ShadowRanger

답변

3

원래 코드는 자신의 코드에 list 선행을 (itertools.product이 발전기를 반환) 만드는 것이 아니라, pool.map 전체 발전기를 실현하고 (모든 출력을 저장할 수있는 경우는 가정하기 때문에, 당신도 모든 입력을 저장할 수 있습니다) .

여기서 pool.map을 사용하지 마십시오. 주문 결과가 필요하거나 pool.imap을 사용하거나 결과 순서가 중요하지 않은 경우 pool.imap_unordered을 사용하십시오.당신이 부작용에 대한 pool.map를 사용하는 경우 그래서,

if __name__ == '__main__': 
    pool = Pool(39) 
    for result in pool.imap(f, itertools.product(pairs, repeat=16)): 
     print(result) 

: 중 하나를 호출의 결과를 반복 (list에 포장하지 않음), 그들이 와서 결과를 처리하고 메모리는 문제가되지 않을한다 결과를 얻고 순서를 매길 필요가 있습니다. imap_unordered을 사용하고 collections.deque을 사용하여 실제로 아무것도 저장하지 않고 "결과"를 효율적으로 사용하여 성능을 크게 향상시킬 수 있습니다. 의 0이 가장 빠릅니다 결과를 저장하지 않고 반복자가 완료까지 실행되도록하는 가장 낮은 메모리 방법) :

from collections import deque 

if __name__ == '__main__': 
    pool = Pool(39) 
    deque(pool.imap_unordered(f, itertools.product(pairs, repeat=16)), 0) 

마지막으로 나는 근로자 39 Pool을 지정하는 것에 대해 조금 의심 스럽습니다. multiprocessing은 CPU 바운드 작업에 크게 유용합니다. CPU 코어보다 많은 인력을 사용하여 이익을 얻는다면 multiprocessing이 IPC에서 얻는 것보다 더 많은 비용을 들일 수 있으며 더 많은 작업자를 사용하면 더 많은 데이터를 버퍼링하여 문제를 숨길 수 있습니다.

작업이 대부분 I/O 바인딩 인 경우 스레드 기반 풀을 사용하면 피클 링 및 unpickling의 오버 헤드와 부모 프로세스와 자식 프로세스 간의 IPC 비용을 피할 수 있습니다. 프로세스 기반 풀과 달리 Python 스레딩은 GIL 문제이므로 CPU가 파이썬에서 작동하도록 제한합니다 (I/O에 대한 GIL 해제 호출, .dll/.so 파일로 ctypes 호출 및 numpy과 같은 특정 타사 확장이 호출 됨). GIL은 단일 코어로 제한됩니다 (CPU 바인딩 작업의 경우 Python 2.x에서는 GIL 해결 문제를 해결하고 컨텍스트 스위치를 수행하기 때문에 Python 3는 대부분의 낭비를 제거합니다). 그러나 작업이 주로 I/O 바인딩 인 경우 I/O를 차단하면 GIL이 해제되어 다른 스레드가 실행될 수 있으므로 I/O가 지연되는 동안 많은 스레드가있을 수 있습니다. "공유"상태로 작성하고 다른 작업자 나 부모 프로세스에는 영향을 미치지 않는다고 가정하여 각 작업자별로 별도의 주소 공간을 사용하도록 프로그램을 설계하지 않은 한 전환 할 수 있습니다.

from multiprocessing.dummy import Pool 

당신은 스레드 대신 프로세스를 기반으로, 풀의 multiprocessing.dummy 버전을 :

from multiprocessing import Pool 

합니다.

+0

설명해 주셔서 감사합니다. 두 가지 옵션을 모두 시도해 보았습니다. 첫 번째 프로세스는 CPU 사용률이 150 % (상단)이며 나머지 프로세스는 40 % 만 사용 중이고 프로세스 수가 증가하면 극적으로 감소합니다 (최대 17 개 39 프로세스 - 40 vcpus). 더 효율적으로 만드는 방법? –

+0

@xis_one :> 1 'chunksize'를 'imap'/ 'imap_unordered'로 전달하면 작업자가 IPC를 다시 차단해야하기 전에 더 많은 작업을 수행 할 수 있습니다. 더 복잡하지만 종종 더 나은 옵션은 근로자가 자신의 작품 중 일부를 생성하도록하는 것입니다. 'pairs'가 전역 변수 인 경우,'product (pairs, repeat = 10)'에 대한 작업을'imap'하고 각 작업자가 마지막 6 개의 가능한 모든 항목을 생성하도록 할 수 있습니다. '(workarg .__add__, product (pairs, repeat = 6)) :'에있는 workitem에 대한 작업을 수행하여 단일 작업을 수행하기 위해 전송해야하는 데이터의 양을 줄입니다. – ShadowRanger

+0

참고 : 마지막 코멘트의'map'은 풀 매핑이 아닌 일반 내장 'map'입니다. Python 2를 사용하고 있다면'future_builtins import map'에서 Py3의 생성기 기반'map'을 사용하여 거대한'list' 문제를 피할 수 있습니다. – ShadowRanger

0

두 번째 코드 예는 한 쌍을 39 개의 작품으로 구성된 풀에 제출하기 때문에 속도가 느립니다. 한 명의 작업자 만 귀하의 요청을 처리하고 나머지 38 명은 아무 것도하지 않습니다! 주 스레드에서 작업자 프로세스로 데이터를 파이프하는 데 오버 헤드가 있으므로 느려질 것입니다.

일부 쌍을 "버퍼링"한 다음 쌍을 실행하여 메모리 사용량의 균형을 조정할 수 있지만 여전히 다중 프로세스 환경을 이용할 수 있습니다.

import itertools 
from multiprocessing import Pool 

def foo(x): 
    return sum(x) 

cpus = 3 
pool = Pool(cpus) 
# 10 is buffer size multiplier - the number of pair that each process will get 
buff_size = 10*cpus 
buff = [] 
for i, r in enumerate(itertools.product(range(20), range(10))): 
    if (i % buff_size) == (buff_size-1): 
     print pool.map(foo, buff) 
     buff = [] 
    else: 
     buff.append(r) 

if len(buff) > 0: 
    print pool.map(foo, buff) 
    buff = [] 

위의 출력은 시스템에 대한 균형을 얻기 위해 buff_size 승수와이

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 2, 3, 4, 5, 6, 7, 8, 9, 10] 
[3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 5, 6, 7, 8, 9, 10, 11, 12, 13] 
[6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 8, 9, 10, 11, 12, 13, 14, 15, 16] 
[9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 11, 12, 13, 14, 15, 16, 17, 18, 19] 
[12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 14, 15, 16, 17, 18, 19, 20, 21, 22] 
[15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 17, 18, 19, 20, 21, 22, 23, 24, 25] 
[18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28] 

플레이처럼 보이는 것!