2017-11-14 2 views
2

다중 처리 라이브러리가있는 큰 데이터 구조에 대해 간단한 계산을하려고합니다. 그것은 내 논문을 위해 필요한 것입니다. 그러니 제발 나를 조심성 거리지 마십시오.다중 처리 Python 라이브러리의 데이터 구조가 느리다

여러 개의 "worker", "threads", "processes"에 대한 계산을 나누거나 원하는대로 호출하기로 결정했을 때 필자는 파이썬 문서를보고 필요한 모듈을 찾은 다음 두 개의 모듈 인 'threading '및'다중 처리 '. 독서 후, 나는 그것이 필요로하는 것처럼 보이기 때문에 '다중 처리'를 사용하기로 결정했습니다.

문제는 여러 작업자 (프로세스)에서 계산이 훨씬 느립니다. 첫 번째 생각은 입력 데이터의 크기와 관련이 있습니다. 작은 데이터의 경우 실행중인 스레드의 '비용'이 단순한 계산보다 훨씬 크다는 것을 이해합니다. 그러나 큰 구조의 경우 효율성이 높아야합니다.

나는 소수의 프로세스로 계산할 때 계산 알고리즘을 사용하는 것보다 계산 알고리즘 (예 : 2D Rosenbrock)이 반복 알고리즘에 비해 몇 배 빨랐다. 계산은 100,000 개의 튜플에 대해 수행됩니다.

나는 또한 다중 처리를 주목했다 .Queue 액세스는 collections.deque에 대한 액세스보다 몇 배나 느리지 만 실제로이 계산을 "공유 메모리"또는 비슷한 것으로해야 할 필요가있다.

어디에서 문제를 설명 할 수 있습니까? 파이썬이 그렇게 효율적이기 때문에 다중 프로세스로 계산할 가치가 없습니까? 적절한 데이터 구조를 사용합니까? 멀티 프로세싱에 대한 인식에서 뭔가를 바꿀 수 있습니까? 또는 어쩌면 나는 나쁜 방법으로 그것을 측정합니까? 나는 속도를 낼 수있는 방법에 대해 정말로 고마워한다.

#!/usr/bin/python 
import multiprocessing 
from timeit import default_timer as timer 
import random 
import collections 

class Worker(multiprocessing.Process): 
    counter = 0 
    def __init__(self, idx, from_queue): 
     super(Worker, self).__init__() 
     self.from_queue = from_queue 
     self.idx = idx 

    def run(self): 
     print ("Worker started", self.idx) 
     for data in iter(self.from_queue.pop, None): 
      x_1, x_2 = data 
      result = 100*(x_2-x_1**2)**2 + (1-x_1)**2 

def main(): 
    tuple_counts = 100000 
    min_x = -5 
    max_x = 5 

    tuples = multiprocessing.Queue() 
    for _ in range(tuple_counts): 
     my_tuple = {random.uniform(min_x, max_x), random.uniform(min_x, max_x)} 
    tuples.put(my_tuple) 

    cores = multiprocessing.cpu_count() - 1 

    pops = [] 
    for _ in range(cores): 
     pop = collections.deque() 
     pops.append(pop) 

    for pop in pops: 
     for _ in range(int(tuple_counts/cores)): 
      pop.append(tuples.get()) 


    for _ in range(int(tuple_counts % cores)): 
     pops[_].append(tuples.get()) 

    for pop in pops: 
     pop.append(None) 

    workers = [] 
    process_time = 0 
    process_time_start = timer() 
    for i in range(multiprocessing.cpu_count()-1): 
     worker = Worker(i, pops[i]) 
     workers.append(worker) 
     worker.start() 
    for worker in workers: 
     worker.join() 
    process_time_stop = timer() 
    process_time += (process_time_stop-process_time_start) 
    print("process_time", process_time) 

    iter_time = 0 
    iter_timer_start = timer() 
    for _ in range(tuples.qsize()): 
     x_1, x_2 = tuples.qet() 
     result = 100*(x_2-x_1**2)**2 + (1-x_1)**2 
    iter_timer_stop = timer() 
    iter_time += (iter_timer_stop-iter_timer_start) 
    print("iter_time", iter_time) 

if __name__ == "__main__": 
    main() 
+0

데이터를 전달하는 것은 일반적으로 피해야 할 대상입니다. 각 작업자에게 처리 할 튜플의 수를 알려주고 무작위 튜플을 독립적으로 생성하고 처리하게하는 것은 무엇입니까? – Blender

+0

내가 이해할 지 모르겠다. 어떻게해야 각 근로자에게 계산을해야한다고 말하면서 100 명의 로젠 브록을 말할 수 있습니까? 물론 논쟁으로 전달하거나 다른 것을 의미 할 수 있습니까? 모든 작업자에 대해 하나의 대기열을 사용하고 있었을 때 나는 그것이 더 느렸다 고 생각합니다. – Tatarinho

+0

큐는 부모 프로세스에서 자식 프로세스로 전달하기 위해 생성 한 모든 튜플을 직렬화 및 비 직렬화해야합니다. 직렬화 및 직렬화 해제는 단순히 결과를 계산하는 것보다 시간이 더 걸릴 것입니다. 일반적으로 이와 같이 데이터를 전달하지 않는 것이 가장 좋습니다. 작업자에게 생성 할 튜플 수를 알려주는 단일 정수를 작업자에게 전달하면됩니다. – Blender

답변

1

아래

전체 코드는 당신은 간단한 계산을 수행하는 프로세스 경계를 ​​넘어 인수를 전달하고 있습니다. 나는 그것이 매우 느릴 것이라고 기대한다.

속도가 필요한 경우 단일 스레드 구현으로 되돌아 가서 numpy를 사용하여 벡터화하는 방법을 찾으십시오. cProfile으로 프로파일 링하십시오. 핫스팟을 공격하십시오.

numpy의 큰 이점은 파이썬 오버 헤드 (이름 확인, 루핑 등) 감소입니다.

일단 단일 스레드 접근 방식을 사용하게되면 병렬 처리로 이동하십시오.

문제를 벡터화 할 때 얻을 수있는 또 다른 이점은 numpy가 길어진 호출에 대해 GIL의 잠금을 해제하여 진정한 스레딩, 대용량 처리를 허용한다는 것입니다.

+0

대신이 튜플을 훨씬 효율적인 방식으로 전달할 수 있습니까? 다른 조언을 해줘서 고맙지 만 더 읽을만한 무언가가 보입니다. – Tatarinho

+1

@ 타틴 인호 : 진지하게 질책을하는 것이 좋습니다. 최적의'multiprocessing' 설정으로도 속도면에서 큰 이득을 얻지 못할 것입니다. 비교를 위해, 순진한 numpy 방식으로 1,000,000 개의 항목을 벡터화 처리하는 데 9.3ms가 소요되고 목록 이해에는 1.53s가 소요됩니다. 다중 처리는 1.53 초를 3 또는 4 배로 줄일 수 있습니다. – Blender