Python 多处理时间随核数的增加而线性增加
我有一个Python 多处理时间随核数的增加而线性增加,python,multiprocessing,arcpy,Python,Multiprocessing,Arcpy,我有一个arcpy过程,需要在一堆层上进行联合,运行一些计算,并编写一个HTML报告。考虑到我需要生成的报告数量(~2100),我需要这个过程尽可能快(我的目标是每个报告2秒)。我已经尝试了很多方法来实现这一点,包括当我遇到一个问题时的多处理,也就是说,无论我使用多少内核,运行多进程部分基本上都需要相同的时间 例如,对于相同数量的报告: 每轮2个核心需要约30秒(因此40份报告需要40/2*30秒) 4芯耗时约60秒(40/4*60) 10芯耗时约160秒(40/10*160) 等等。计算出
arcpy
过程,需要在一堆层上进行联合,运行一些计算,并编写一个HTML报告。考虑到我需要生成的报告数量(~2100),我需要这个过程尽可能快(我的目标是每个报告2秒)。我已经尝试了很多方法来实现这一点,包括当我遇到一个问题时的多处理,也就是说,无论我使用多少内核,运行多进程部分基本上都需要相同的时间
例如,对于相同数量的报告:
- 每轮2个核心需要约30秒(因此40份报告需要40/2*30秒)
- 4芯耗时约60秒(40/4*60)
- 10芯耗时约160秒(40/10*160)
## Worker function just included to give some context
def worker(sub_code):
layer = 'in_memory/lyr_{}'.format(sub_code)
arcpy.Select_analysis(subbasinFC, layer, where_clause="SUB_CD = '{}'".format(sub_code))
arcpy.env.extent = layer
union_name = 'in_memory/union_' + sub_code
arcpy.Union_analysis([fields],
union_name,
"NO_FID", "1 FEET")
#.......Some calculations using cursors
# Templating using Jinjah
context = {}
context['DATE'] = now.strftime("%B %d, %Y")
context['SUB_CD'] = sub_code
context['SUB_ACRES'] = sum([r[0] for r in arcpy.da.SearchCursor(union, ["ACRES"], where_clause="SUB_CD = '{}'".format(sub_code))])
# Etc
# Then write the report out using custom function
write_html('template.html', 'output_folder', context)
if __name__ == '__main__':
subList = sorted({r[0] for r in arcpy.da.SearchCursor(subbasinFC, ["SUB_CD"])})
NUM_CORES = 7
chunk_list = [subList[i:i+NUM_CORES] for i in range(0, len(subList), NUM_CORES-1)]
for chunk in chunk_list:
jobs = []
for subbasin in chunk:
p = multiprocessing.Process(target=worker, args=(subbasin,))
jobs.append(p)
p.start()
for process in jobs:
process.join()
我不确定您是否正确使用
流程
池来跟踪您的作业。这:
for subbasin in chunk:
p = multiprocessing.Process(target=worker, args=(subbasin,))
jobs.append(p)
p.start()
for process in jobs:
process.join()
应改为:
for subbasin in chunk:
p = multiprocessing.Process(target=worker, args=(subbasin,))
p.start()
p.join()
你反对这项法案有什么具体原因吗?您不会等到线程终止后再启动另一个进程,这只会创建一大堆不由父调用进程处理的进程。我不确定您是否正确使用
进程池来跟踪作业。这:
for subbasin in chunk:
p = multiprocessing.Process(target=worker, args=(subbasin,))
jobs.append(p)
p.start()
for process in jobs:
process.join()
应改为:
for subbasin in chunk:
p = multiprocessing.Process(target=worker, args=(subbasin,))
p.start()
p.join()
你反对这项法案有什么具体原因吗?您不会等到线程终止后再启动另一个进程,这只会创建一大堆不由父调用进程处理的进程。这里没有太多内容,我也没有使用ArcGIS的经验。所以我可以注意到两个更高层次的东西。首先,“通常”的方法是将NUM_CORES=7
下面的所有代码替换为:
pool = multiprocessing.Pool(NUM_CORES)
pool.map(worker, subList)
pool.close()
pool.join()
map()。按原样,启动7个进程,然后等待它们全部完成。在最慢的进程消失之前完成的所有进程都将消失,它们的核心将闲置等待下一个外循环迭代。池
使7个进程在作业期间保持活动状态,并在每个进程完成其最后一项工作后为其提供一项新工作
其次,此部分以逻辑错误结束:
chunk_list = [subList[i:i+NUM_CORES] for i in range(0, len(subList), NUM_CORES-1)]
您想要NUM\u CORES
而不是NUM\u CORES-1
。就这样,在你身边的第一时间
subList[0:7]
然后
然后
等等<代码>子列表[6]
和子列表[12]
(etc)分别提取两次。子列表重叠。这里没有太多内容,我也没有使用ArcGIS的经验。所以我可以注意到两个更高层次的东西。首先,“通常”的方法是将NUM_CORES=7
下面的所有代码替换为:
pool = multiprocessing.Pool(NUM_CORES)
pool.map(worker, subList)
pool.close()
pool.join()
map()。按原样,启动7个进程,然后等待它们全部完成。在最慢的进程消失之前完成的所有进程都将消失,它们的核心将闲置等待下一个外循环迭代。池
使7个进程在作业期间保持活动状态,并在每个进程完成其最后一项工作后为其提供一项新工作
其次,此部分以逻辑错误结束:
chunk_list = [subList[i:i+NUM_CORES] for i in range(0, len(subList), NUM_CORES-1)]
您想要NUM\u CORES
而不是NUM\u CORES-1
。就这样,在你身边的第一时间
subList[0:7]
然后
然后
等等<代码>子列表[6]
和子列表[12]
(etc)分别提取两次。子列表重叠。您没有向我们展示足够的信息来确定您在做什么。例如,您的环境工作区是什么?subbasinFC
的值是多少?似乎您在每个流程的开始处都在进行分析,以将数据过滤到层
。但是subbasinFC
是来自磁盘还是内存?如果它来自磁盘,我建议您在任何进程尝试过滤之前将所有内容读入内存。如果你有足够的内存支持的话,这会加快事情的发展。否则,是的,输入数据将被I/O绑定
请原谅我的arcpy
cluelessness,但是为什么要在上下文['SUB_ACRES']
的总和中插入where子句呢?你不是一开始就已经过滤了子代码
?(我们不知道工会是什么,所以可能你正在与未经过滤的东西建立工会…你没有向我们展示足够的信息来确定你在做什么。例如,您的环境工作区是什么?subbasinFC
的值是多少?似乎您在每个流程的开始处都在进行分析,以将数据过滤到层
。但是subbasinFC
是来自磁盘还是内存?如果它来自磁盘,我建议您在任何进程尝试过滤之前将所有内容读入内存。如果你有足够的内存支持的话,这会加快事情的发展。否则,是的,输入数据将被I/O绑定
请原谅我的arcpy
cluelessness,但是为什么要在上下文['SUB_ACRES']
的总和中插入where子句呢?你不是一开始就已经过滤了子代码
?(我们不知道联合是什么,所以可能您正在与未过滤的内容联合……有多少个核心