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

2024-10-03 02:40:55 发布

您现在位置:Python中文网/ 问答频道 /正文

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

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

为此,我尝试了两种方法。在

第一种方法

  1. 拉动主线程中的所有位置和所有用户
  2. 遍历所有用户并生成x线程(在我的小MacBook8上似乎是最好的)

    with cf.ThreadPoolExecutor(max_workers=8) as executor:
        futures = [executor.submit(task,userId, placeIds) for userId in userIds]
    

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

  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]

如您所见,因为CalculateStores本身将位于8//线程上,因此我无法共享数据库连接,否则我将得到竞争条件错误(然后脚本将崩溃1/3/4)

这个方法,我以为会更快,但是需要25分钟。。。。。用简单的for循环代替10…)

我有90%的把握这是比较慢的,因为现在每个任务都从池中得到一个数据库连接,这是非常昂贵的,因此速度慢。。在

有人能给我一些建议,告诉我在我的场景中,什么是最好的并行化方法?在

这是使任务返回结果的好主意吗?还是应该在calculateScores函数中一准备好就把它们插入数据库?在

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

我应该试着把多个过程付诸行动吗?在

谢谢你!在


Tags: 方法用户数据库列表forconn线程cursor
1条回答
网友
1楼 · 发布于 2024-10-03 02:40:55

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

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

时间性能在很大程度上取决于您如何组织db操作。见Improve INSERT-per-second performance of SQLite?

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

相关问题 更多 >