我在这里有一个关于在python字典上进行计算的问题-在这种情况下,字典有数百万个键,并且列表也很长。在这里是否可以使用并行化似乎存在分歧,因此在这里我将更明确地提出问题。这是原始问题:
Optimizing parsing of massive python dictionary, multi-threading
这是玩具(小)python字典:
example_dict1 = {'key1':[367, 30, 847, 482, 887, 654, 347, 504, 413, 821],
'key2':[754, 915, 622, 149, 279, 192, 312, 203, 742, 846],
'key3':[586, 521, 470, 476, 693, 426, 746, 733, 528, 565]}
假设我需要解析列表的值,我已经将其实现到以下简单(玩具)函数中:
def manipulate_values(input_list):
return_values = []
for i in input_list:
new_value = i ** 2 - 13
return_values.append(new_value)
return return_values
现在,我可以轻松地按如下方式解析此字典的值:
for key, value in example_dict1.items():
example_dict1[key] = manipulate_values(value)
导致以下结果:
example_dict1 = {'key1': [134676, 887, 717396, 232311, 786756, 427703, 120396, 254003, 170556, 674028],
'key2': [568503, 837212, 386871, 22188, 77828, 36851, 97331, 41196, 550551, 715703],
'key3': [343383, 271428, 220887, 226563, 480236, 181463, 556503, 537276, 278771, 319212]}
问题:为什么我不能使用多个线程来进行此计算,例如三个线程,一个用于key1
,key2
和key3
? concurrent.futures.ProcessPoolExecutor()
在这里可以工作吗?
原始问题:是否有更好的方法来快速优化此过程?
python线程实际上并不会真正帮助您并行处理,因为它们是在同一“真实CPU线程”上执行的,当您处理异步HTTP调用时,python线程会很有帮助]
关于ProcessPoolExecutor
形成docs:
concurrent.futures.ProcessPoolExecutor()
ProcessPoolExecutorclass是一个Executor子类,它使用进程池来执行异步调用。 ProcessPoolExecutor使用多处理模块,它可以回避全局解释器锁定,但是也意味着只能执行和返回可拾取对象。
如果需要较高的CPU处理能力,可以使用:
import concurrent
def manipulate_values(k_v):
k, v = k_v
return_values = []
for i in v :
new_value = i ** 2 - 13
return_values.append(new_value)
return k, return_values
with concurrent.futures.ProcessPoolExecutor() as executor:
example_dict = dict(executor.map(manipulate_values, example_dict1.items()))
Q:“ 为什么我不能使用多个线程来进行此计算,例如,三个线程,一个用于key1,key2和key3吗?”
您可以,但是对性能没有任何合理的影响-了解有关python如何处理基于线程的执行流的所有详细信息,这是至关重要的。 Learn about the GIL-lock trick,直接用于避免任何并发处理,effects on performance则获得WHY部分。
Q:“ 将
concurrent.futures.ProcessPoolExecutor()
在这里工作吗?”
将。
然而,其净效果(如果有比纯-[SERIAL]
处理流程更“更快”)将取决于“大”-列表的给定大小(警告为(cit。)“数百万个键,并且列表也一样长。”)应该复制(RAM-I / O)并通过(SER / DES-已处理+ IPC转移到已生成的(基于进程的)远程执行程序池。
许多次重复的RAM-I / O + SER / DES附加开销成本将很快占主导地位。
RAM-I / O复制步骤:
>>> from zmq import Stopwatch; aClk = Stopwatch()
>>> aClk.start(); aList = [ i for i in range( int( 1E4 ) ) ]; aClk.stop()
1345 [us] to copy a List of 1E4 elements
>>> aClk.start(); aList = [ i for i in range( int( 1E5 ) ) ]; aClk.stop()
12776 [us] to copy a List of 1E5 elements
>>> aClk.start(); aList = [ i for i in range( int( 1E6 ) ) ]; aClk.stop()
149197 [us] to copy a List of 1E6 elements
>>> aClk.start(); aList = [ i for i in range( int( 1E7 ) ) ]; aClk.stop()
1253792 [us] to copy a List of 1E7 elements
| |::: [us]
| +--- [ms]
+------ [ s]
SER / DES步骤:
>>> import pickle
>>> aClk.start(); _ = pickle.dumps( aList ); aClk.stop()
608323
615851
638821 [us] to copy pickle.dumps() a List of 1E7 elements
| |::: [us]
| +--- [ms]
+------ [ s]
因此,预期的每批附加费用为~ 2 x ( 1253 + 608 ) [ms] +
IPC转移仅需一枪1E7项的费用
[ “ there”manipulate_values()
]的实际有用工作量很小,以至于所有附加成本的总和几乎都无法弥补与在整个工作池中分配工作单位相关的附加支出。 -远程工作者。从矢量化的计算形式可以期待更智能的结果。这里的附加成本远远高于少量的有用工作。更多的模式将取决于SER / DES参数传递