如何在Python中并行化列表元素

时间:2015-02-16 10:11:32

标签: python numpy matrix parallel-processing threadpool

我有很多字典。我想从这个列表中计算一个二维矩阵,其中每个元素是列表中第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.mapmultiprocessing,但计算花费的时间比原始代码要长。我试过像:

    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()

但它比原始代码需要更多时间。我哪里错了?谢谢

1 个答案:

答案 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解释器执行。

结论 - 你应该如何处理这个

您不需要多个流程。您需要在一个进程中使用正确的方法(线程,甚至)。你需要:

  1. 更高效的数据结构
  2. 基于线性代数的算法
  3. 用于执行此算法的高度优化的数学库
  4. 如果你以正确的方式接近这一点,我可以向你保证,你的代码可以更快地运行数量级。因为引擎盖下的numpy / scipy利用了基于矢量化的CPU功能(SIMD,"单指令,多数据和#34;),它们将以尽可能快的速度运行您的数据操作。并且,如果您的矩阵足够大,以便您的算法在理论上可以利用多个CPU核心:甚至在numpy和scipy中的OpenMP支持,某些矩阵操作例程可以自动在多个线程上分发他们的工作