Python,帮助并行化算法(尝试在线程池中包含线程池

Python,帮助并行化算法(尝试在线程池中包含线程池,python,multithreading,performance,python-3.x,concurrent.futures,Python,Multithreading,Performance,Python 3.x,Concurrent.futures,我正在尝试将一些计算并行化,但我不明白为什么我的一个版本(我认为应该更快)比这个版本慢 简而言之,我有一个userid列表(大约200个)和一个placesId列表(大约200000个)。我需要为每对用户/位置计算一个分数。好事情 计算是完全独立的(取决于我们如何实现算法,甚至不需要返回结果) 我为此尝试了两种方法 第一种方法 拉动主线程中的所有位置和所有用户 循环遍历所有的user和spawn x线程(在我的例子中,我的小macbook 8似乎是最好的) 当所有的未来都完成后,我循环遍历所有的

我正在尝试将一些计算并行化,但我不明白为什么我的一个版本(我认为应该更快)比这个版本慢

简而言之,我有一个userid列表(大约200个)和一个placesId列表(大约200000个)。我需要为每对用户/位置计算一个分数。好事情 计算是完全独立的(取决于我们如何实现算法,甚至不需要返回结果)

我为此尝试了两种方法

第一种方法

  • 拉动主线程中的所有位置和所有用户
  • 循环遍历所有的user和spawn x线程(在我的例子中,我的小macbook 8似乎是最好的)

    当所有的未来都完成后,我循环遍历所有的未来并插入结果 在数据库中(worker任务返回一个列表[userId,placeId,score])

  • 我有一个任务,它将遍历所有位置并返回结果

    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//个线程上,因此我无法共享数据库连接,否则我将出现竞争条件错误(然后脚本将在4次中的3次中有1次崩溃)

    这种方法,我认为会更快,但需要25分钟。。。。。(而不是使用简单for循环的10…)

    我90%确信这会比较慢,因为现在每个任务都从池中获得一个数据库连接,这不知何故非常昂贵,因此速度较慢

    有没有人能给我一些建议,让我的场景中的并行化发挥最大作用的最佳方式是什么

    这是使任务返回结果的好主意吗?或者我应该在calculateScores函数中准备好后立即将它们插入数据库中吗

    在线程池中有一个线程池是一种好的做法吗

    我是否应该尝试实施一些多流程

    谢谢大家!

    在线程池中有一个线程池是一种好的做法吗

    不,在您的情况下,单线程池就足够了,例如:

    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绑定的,那么线程可以提高时间性能(),因为可以在python本身的i/O(和其他阻塞操作系统)调用过程中或在C扩展(如用于CPython的db驱动程序)中释放GIL

    如果数据库能够很好地处理并发访问,那么每个线程都可以使用自己的数据库连接。注意:
    8
    线程可能比
    4
    16
    线程都快——您需要测量它

    时间性能可能在很大程度上取决于如何构造数据库操作。看


    如果任务受CPU限制,例如,您为每个用户/位置id执行一些昂贵的纯Python计算,那么您可以尝试
    ProcessPoolExecutor
    而不是
    ThreadPoolExecutor
    。确保进程之间输入/输出数据的复制不会主导计算本身。

    您知道GIL吗?我所知道的最好的解释是大卫·比兹利(David Beazley):我以为我做到了,但显然没有。他举的第一个例子就是为什么我的第二种方法比较慢。。。很明显,这是没有办法的?即使我开始在同一句话中使用进程-线程和Python。。。不幸的是,这项任务是由Java完成的,我认为使用python会更快(是这样的(Java版本为20分钟)),但我认为我可以用这种方法来推动它2@AlexGidan字体拙劣的工匠责备他的工具。Python+线程在很多工作中都很有用。
    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)