1

일부 계산을 병렬 처리하려고하는데 왜 내 버전 (내가 더 빨라야한다고 생각했는지)이 느린 이유를 이해하지 못합니다.Python, 알고리즘을 병렬 처리하는 데 도움이 됨 (스레드 풀 내부에서 스레드 풀을 갖기 위해 노력)

간략하게 말하자면, userIds 목록 (200 개 이하)과 placesId (2,000,000 개 이하) 목록이 있습니다. 각 쌍 사용자/장소에 대한 점수를 계산해야합니다. 은 계산이 서로 완전히 독립적이며 알고리즘을 구현하는 방법에 따라 결과가 더 필요하지 않다는 결론을 얻었습니다.

나는 이것을 위해 2 가지 방법을 시도했습니다.

,451,515,

첫 번째 방법

  1. 풀 ALL 내 작은 맥북 8 내 경우에는
  2. 루프 모든 사용자와 산란 X 스레드를 통해 메인 스레드에서 장소와 모든 사용자가 (될 것으로 보인다) 최고의

    모든 선물이 (작업자의 작업 목록 [userId를, placeId를 돌려 그들 모두를 통해 I 루프를 완료하고 데이터베이스에 을 결과를 삽입하는
    with cf.ThreadPoolExecutor(max_workers=8) as executor: 
        futures = [executor.submit(task,userId, placeIds) for userId in userIds] 
    

    , 점수])

  3. 나는 모든 장소를 통해 루프는이 여자와 부드러운 사람이 계산되는 사용자/장소의 모든 설정을 만드는 결과

    def task(userId, placeIds): 
        connection = pool.getconn() 
        cursor = conn.cursor() 
        #loop through all the places and call makeCalculation(cur, userId, placeId) 
        pool.putconn(conn) 
        return results 
    

을 반환하는 작업을 10 분 (순차적 방식으로 1.30 시간 대신)

그러나 그렇다면 왜 점수 계산을 병렬 처리할까요? 따라서 2000 개의 모든 장소를 한 번씩 반복해야하는 작업 대신 다른 8 개의 스레드에 계산을 생성하십시오.

두 번째 방법 :

기본적으로이 방식에 의해 "작업"함수에서 루프를 대체 : 내가해야 할 일을했을

with concurrent.futures.ThreadPoolExecutor(max_workers=8) as executor: 
    futures = [ executor.submit(calculateScores,userId,placeId) for placeId in placeIds] 

다른 수정은 calculateScores이 기능에

def calculateScores(userId,placeId): 
    **connection = pool.getconn() 
    cursor = connecton.cursor()** 
    ... 
    make a bunch of calculation by calling the database 1 or 2 times 

    pool.putconn(conn) 
    return [userId, placeId, score] 
이제 calculateScores 자체가 8 //에 있기 때문에 볼 수 있듯이 // thr eads 그래서 나는 데이터베이스 연결을 공유 할 수 없다. 그렇지 않으면 경합 조건 오류가 발생한다. (그리고 스크립트는 4 번 중 3 번 중 하나가 충돌 할 것이다)

이 접근법은 더 빠른 bu가 25 분이 소요될 것이라고 생각했다. ... (단순한 for 루프를 사용하는 10 대신 ...)

모든 작업이 이제 풀에서 데이터베이스 연결을 가져오고 이것이 다소 비용이 많이 들고 느려서 느려지므로 90 %가 느립니다.

내 시나리오에서 병렬 처리를 최대한 활용하는 가장 좋은 방법은 누군가에게 조언을 줄 수 있습니까?

작업 결과를 얻는 것이 좋습니까? 또는 calculateScores 함수가 준비되면 바로 데이터베이스에 삽입해야합니까?

ThreadPool 안에 Threadpool을 설치하는 것이 좋습니다.

몇 가지 다중 프로세스를 실행해야합니까?

감사합니다.

+3

GIL을 알고 있습니까? 제가 아는 가장 좋은 설명은 David Beazley입니다 : http://www.dabeaz.com/python/UnderstandingGIL.pdf – cdarke

+0

제가 생각하기는하지만 분명히 아닙니다. 첫 번째 예제는 두 번째 접근 방식이 더 느린 이유에 대한 부분입니다. 프로세스를 사용하기 시작했다해도? – Johny19

+1

같은 문장의 스레드와 파이썬 ... 나쁜 징조 –

답변

1

ThreadPool 안에 Threadpool을 설치하는 것이 좋습니다.

아니, 단일 스레드 풀은 사건의 예에 충분하다 : 데이터베이스 응용 프로그램에서 병목 현상 인 경우

from concurrent.futures import ThreadPoolExecutor as Executor 
from collections import deque 

with Executor(max_workers=8) as executor: 
    deque(executor.map(calculateScores, userIds, placeIds), maxlen=0) 

(알아, 당신은 DB 호출을 조롱 수) 즉, 경우 작업은 I/O bound이고 그 다음에 스레드 GIL은 I/O (및 다른 블로킹 OS) 호출시 Python 자체 또는 CPython의 db 드라이버와 같은 C 확장자로 호출 될 수 있으므로 시간 성능을 향상시킵니다 (to a point).

데이터베이스가 동시 액세스를 잘 처리하면 각 스레드는 자체 db 연결을 사용할 수 있습니다. 참고 : 8 스레드는 4 스레드와 16 스레드보다 빠를 수 있습니다. 스레드를 측정해야합니다.

시간 성능은 DB 조작을 구조화하는 방법에 따라 크게 달라질 수 있습니다. Improve INSERT-per-second performance of SQLite?

예를 들어 작업이 CPU에 바인딩되어있는 경우 각 사용자/작업 공간 ID에 대해 비싼 순수 Python 계산을 수행하면 ThreadPoolExecutor 대신 ProcessPoolExecutor을 시도 할 수 있습니다. 프로세스간에 입력/출력 데이터를 복사하는 것이 계산 자체를 지배하지 않는지 확인하십시오.

관련 문제