我有一个复杂的嵌套数据结构。我遍历它,并对每个可能的uniqe对元素执行一些计算。所有这些都是内存中的数学函数。我不会读取文件或进行联网。
运行需要几个小时,do_work()
被调用了25,000次。我正在寻找加快速度的方法。
尽管Pool.map()
对于我的列表似乎很有用,但事实证明这很困难,因为我需要将额外的参数传递给要映射的函数。
我认为使用Python multitasking
库会有所帮助,但是当我使用Pool.apply_async()
调用do_work()
时,它实际上会花费更长的时间。
我做了一次谷歌搜索,a blogger说,“将同步用于内存中操作-当您不进行阻塞呼叫时,异步完全是浪费。” 是吗?有人可以解释为什么吗? RAM的读写操作是否相互干扰?为什么异步调用需要花费更长的时间? do_work()
将计算结果写入数据库,但不会修改我的数据结构。
当然,有一种方法可以利用我的处理器内核,而不仅仅是线性地遍历列表。
我的起点,同步进行:
main_list = [ [ [a,b,c,[x,y,z], ... ], ... ], ... ] # list of identical structures
helper_list = [1,2,3]
z = 2
for i_1 in range(0, len(main_list)):
for i_2 in range(0, len(main_list)):
if i_1 < i_2: # only unique combinations
for m in range(0, len(main_list[i_1])):
for h, helper in enumerate(helper_list):
do_work(
main_list[i_1][m][0], main_list[i_2][m][0], # unique combo
main_list[i_1][m][1], main_list[i_1][m][2],
main_list[i_1][m][3][z], main_list[i_2][m][3][h],
helper_list[h]
)
更改了变量名称,使其更具可读性。
答案 0 :(得分:4)
这只是一个普遍的答案,但是对于评论来说太长了……
首先,我认为您目前最大的瓶颈是Python本身。我不知道do_work()
的作用,但是如果它占用大量CPU,则可以使用GIL来完全阻止一个进程内部的有效并行化。不管您做什么,线程都会为GIL争斗,最终将使您的代码变得更慢。请记住:Python具有真正的线程,但是CPU在单个进程内共享。
我建议您查阅David M Beazley的页面:http://dabeaz.com/GIL/gilvis,他做了很多工作来可视化Python中的GIL行为。
另一方面,模块multiprocessing
允许您运行多个进程并“规避” GIL的缺点,但是要获得对相同内存位置的访问而又不会产生更大的代价或权衡,将非常棘手。
第二:如果您使用沉重的嵌套循环,则应考虑使用numba
并尝试将数据结构放入numpy
(结构化)数组中。这可以使您轻松获得数量级的速度。对于此类事情,Python实在是太慢了,但是幸运的是,当使用适当的库时,有很多方法可以挤出很多。
总而言之,我认为您运行的代码在numba
和numpy
结构下可以快几个数量级。
或者,您可以尝试使用Julia
之类的语言(与Python非常相似的语法重写该代码,并且该社区非常有用),并快速检查其速度以探究性能极限。 。感觉到某种东西(或代码的一部分)在没有像Python这样复杂的性能关键方面的语言中可以有多快,这总是一个好主意。
答案 1 :(得分:2)
您的任务比依赖I / O操作的CPU资源更多。当您进行长时间的I / O操作(即从网络等发送/接收内容)时,异步执行才有意义。
您可以做的是将任务拆分为多个块,并利用线程和多处理功能(在不同的CPU内核上运行)。