我有很多字典。我想从这个列表中计算一个二维矩阵,其中每个元素是列表中第i个元素和列表中第j个元素的函数。代码是这样的:
matrix=np.array([])
biglist=self.some_method()
matrix.resize(len(biglist),len(biglist))
for i in range(len(biglist)):
for j in range(i,len(biglist)):
matrix[i,j]=self.__computeScore(biglist[i], biglist[j], i, j)[2]
matrix[j,i]=matrix[i,j]
现在的__computeScore
方法非常简单:
def __computeScore(self, dictionary1, dictionary2, i, j):
#value=in future some computation over dictionary1 and dictionary2
value=1 #for now is so
return (i,j,value)
即使计算得分的方法非常简单,现在计算矩阵也需要一段时间。我想并行化矩阵计算。什么是最好的方法?到目前为止,我已经尝试使用apply_async
模块中的Pool.map
和multiprocessing
,但计算花费的时间比原始代码要长。我试过像:
pool = multiprocessing.Pool(processes=4)
params=[]
print "argument creation" #this takes a while so it's not convinient
for i in range(len(biglist)):
for j in range(i,len(biglist)):
params.append((biglist[i],biglist[j],i,j))
result=pool.map(self.__computeScore, params) #takes more time than the original code
我也尝试过这样的事情:
def my_callback( result ):
matrix[result[0],result[1]]=result[2]
pool = multiprocessing.Pool(processes=4)
for i in range(len(biglist)):
rcopy=dict(biglist[i])
for j in range(i,len(biglist)):
ccopy=dict(biglist[j])
pool.apply_async(self.__computeScore, args=(rcopy, ccopy, i, j), callback = my_callback)
pool.close()
pool.join()
但它比原始代码需要更多时间。我哪里错了?谢谢
答案 0 :(得分:3)
我哪里错了?
假设matrix[i,j]=self.__computeScore(...)
级别的多个进程之间的并行化将为您带来显着的性能提升。
此外,假设对代码进行微小修改会导致显着加快速度。这不一定是" math"像你这样的问题。这通常需要您重新构建算法,包括更高效的数据结构。
事实上,你所观察到的是,与将事物保存在一个进程甚至线程中相比,产生进程和向它们传递任务带来了巨大的开销。与任务所需的计算时间相比,传达任务所花费的时间要少得多,这种开销只能带来回报。您的基于多处理的方法大部分时间都花在进程间通信上,__computeScore(self, dictionary1, dictionary2, i, j)
很可能只会对子进程感到厌烦。一个反例:假设你给一个进程一个文件名,然后这个进程花费15秒来处理从该文件读取数据并处理该数据。传输文件名只需几微秒。因此,在这种情况下,定义"所需的时间。在执行实际工作所花费的时间内,工作可忽略不计。 此是多处理闪耀的场景。它实际上非常简单,特别是一旦了解了多处理技术的工作原理。但是,不幸的是,你在这里的误解很常见。
你的情况完全不同。您需要了解计算机如何工作以了解如何优化您的应用程序案例。首先,你应该防止复制RAM中的大量数据,特别是在进程空间之间!您正在操作的大数据应存储在一次内存中,存储在高效的数据结构中(即,在允许以高效率执行所有后续操作的数据结构中)方式)。然后你需要意识到Python本身并不是一门快速的语言。解决方案不是使用更快的高级语言,而是要外部化"外部化"尽可能对编译代码执行性能关键代码 - 你拥有的双重嵌套循环是一个强有力的指标。此循环应在已编译的例程中执行。事实上,您已经将问题视为线性代数问题,需要更频繁地使用线性代数包(您已使用numpy
,但不是因此)。我看到你需要循环,因为很多你的" base"数据在列表或词典中。我看待它的方式,你需要找到一种方法来摆脱这些列表/ dicts,并根据numpy数据类型定义你拥有的所有数据。这样,您可以找到一种简单的方法来使O(N ^ 2)操作由机器代码执行,而不是由Python解释器执行。
您不需要多个流程。您需要在一个进程中使用正确的方法(线程,甚至)。你需要:
如果你以正确的方式接近这一点,我可以向你保证,你的代码可以更快地运行数量级。因为引擎盖下的numpy / scipy利用了基于矢量化的CPU功能(SIMD,"单指令,多数据和#34;),它们将以尽可能快的速度运行您的数据操作。并且,如果您的矩阵足够大,以便您的算法在理论上可以利用多个CPU核心:甚至在numpy和scipy中的OpenMP支持,某些矩阵操作例程可以自动在多个线程上分发他们的工作。