如何在Python中并行化列表理解计算?

时间:2011-03-08 17:57:13

标签: python parallel-processing list-comprehension

列表推导和地图计算都应该 - 至少在理论上 - 相对容易并行化:列表理解中的每个计算都可以独立于所有其他元素的计算来完成。例如,在表达式

[ x*x for x in range(1000) ]

每个x * x-计算可以(至少在理论上)并行完成。

我的问题是:是否有任何Python-Module / Python-Implementation / Python Programming-Trick来并行化列表理解计算(为了使用所有16​​/32 / ...核心或通过计算机分配计算 - 网格或云端)?

8 个答案:

答案 0 :(得分:29)

正如肯所说,它不能,但是使用2.6的multiprocessing模块,并行计算很容易。

import multiprocessing

try:
    cpus = multiprocessing.cpu_count()
except NotImplementedError:
    cpus = 2   # arbitrary default


def square(n):
    return n * n

pool = multiprocessing.Pool(processes=cpus)
print(pool.map(square, range(1000)))

documentation中还有一些示例说明如何使用Managers执行此操作,这也应该允许分布式计算。

答案 1 :(得分:8)

关于列表理解的自动并行化

如果没有其他信息(例如在OpenMP中使用指令提供的信息),或者将其限制为仅涉及内置类型/方法的表达式,恕我直言,有效列表理解的自动并行是不可能的。

除非保证对每个列表项进行的处理没有副作用,否则如果无序执行,结果可能无效(或至少不同)。

# Artificial example
counter = 0

def g(x): # func with side-effect
    global counter
    counter = counter + 1
    return x + counter

vals = [g(i) for i in range(100)] # diff result when not done in order

还有任务分配问题。问题空间应该如何分解?

如果每个元素的处理形成一个任务(〜任务场),那么当有许多元素都涉及琐碎的计算时,管理任务的开销将淹没并行化的性能提升。

还可以采用数据分解方法,在可用流程中将问题空间平均分配。

列表理解也适用于生成器这一事实使得这有点棘手,但如果预迭代它的开销是可接受的,这可能不是一个显示停止。当然,如果后续项目过早地重复,则还存在具有副作用的发生器的可能性,其可以改变结果。非常不可能,但可能。

更大的担忧是跨流程的负载不平衡。无法保证每个元素都需要相同的时间来处理,因此静态分区数据可能会导致一个进程执行大部分工作,而闲置您的时间。

将列表分解为较小的块并在每个子进程可用时处理它们是一个很好的折衷方案,但是,如果没有来自用户的更多信息,则很好地选择块大小将取决于应用程序,因此无法实现。

替代

正如其他几个答案中所提到的,根据一个要求,有许多方法和parallel computing modules/frameworks可供选择。

仅使用过没有使用Python进行并行处理的经验的MPI(在C中),我无法保证任何(尽管快速扫描, multiprocessingjugpppyro脱颖而出)。

如果要求尽可能贴近列表理解,那么jug似乎是最接近的匹配。从tutorial开始,跨多个实例分发任务可以很简单:

from jug.task import Task
from yourmodule import process_data
tasks = [Task(process_data,infile) for infile in glob('*.dat')]

虽然这与multiprocessing.Pool.map()类似,但jug可以使用不同的后端来同步进程并存储中间结果(redis,文件系统,内存中),这意味着进程可以跨越节点群集。

答案 2 :(得分:5)

使用新的3.2 concurrent.futures软件包中的futures.{Thread,Process}PoolExecutor.map(func, *iterables, timeout=None)futures.as_completed(future_instances, timeout=None)函数可能有所帮助。

它也可以2.6 + backport

答案 3 :(得分:4)

对于共享内存并行性,我建议joblib

from joblib import delayed, Parallel

def square(x): return x*x
values = Parallel(n_jobs=NUM_CPUS)(delayed(square)(x) for x in range(1000))

答案 4 :(得分:3)

不,因为列表理解本身就是一种C优化的宏。如果你将它拉出并并行化,那么它不是列表理解,它只是一个很好的老式MapReduce

但您可以轻松地并行化您的示例。这是一个关于在Python的并行化库中使用MapReduce的好教程:

http://mikecvet.wordpress.com/2010/07/02/parallel-mapreduce-in-python/

答案 5 :(得分:1)

这里有一个完整的Python并行包列表:

http://wiki.python.org/moin/ParallelProcessing

我不确定是否有任何处理直接拆分列表推导构造,但以非列表理解方式制定相同的问题应该是很容易的,可以很容易地分叉到许多不同的处理器。我不熟悉云计算并行化,但我在多核机器和集群上使用mpi4py取得了一些成功。您需要考虑的最大问题是通信开销是否会破坏并行化问题所带来的收益。

修改:以下内容可能也会引起您的兴趣:

http://www.mblondel.org/journal/2009/11/27/easy-parallelization-with-data-decomposition/

答案 6 :(得分:1)

不在列表理解AFAIK中。

你当然可以使用传统的for循环和多处理/线程模块来完成它。

答案 7 :(得分:1)

正如以上答案所指出的那样,这实际上很难自动完成。然后我认为问题实际上是如何以最简单的方式做到这一点。理想情况下,解决方案不需要您了解诸如“我拥有多少个内核”之类的知识。您可能想要的另一个属性是仍然能够在单个可读行中进行列表理解。

某些给出的答案似乎已经具有类似这样的好属性,但是另一种选择是Ray(docs),这是用于编写并行Python的框架。在Ray中,您可以这样做:

import ray

# Start Ray. This creates some processes that can do work in parallel.
ray.init()

# Add this line to signify that the function can be run in parallel (as a
# "task"). Ray will load-balance different `square` tasks automatically.
@ray.remote
def square(x):
    return x * x

# Create some parallel work using a list comprehension, then block until the
# results are ready with `ray.get`.
ray.get([square.remote(x) for x in range(1000)])