在IPython中并行嵌套for循环

时间:2012-02-20 14:54:51

标签: python parallel-processing ipython

我的python代码中有一个嵌套的for循环,看起来像这样:

results = []

for azimuth in azimuths:
    for zenith in zeniths:
        # Do various bits of stuff
        # Eventually get a result
        results.append(result)

我想在我的4核机器上并行化这个循环来加速它。看看IPython并行编程文档(http://ipython.org/ipython-doc/dev/parallel/parallel_multiengine.html#quick-and-easy-parallelism),似乎有一种简单的方法可以使用{{1}并行化迭代操作。

但是,要做到这一点,我需要将循环中的代码作为一个函数(这很容易),然后映射到这个函数。我遇到的问题是我无法获得一个数组来映射这个函数。 map生成一个迭代器,我似乎无法使用map函数。

我试图在这里使用地图咆哮错误的树吗?有没有更好的方法呢?或者是否有某种方法可以使用itertools.product()然后使用映射到结果的函数进行并行执行?

5 个答案:

答案 0 :(得分:10)

要并行化每个调用,您只需要为每个参数获取一个列表。您可以使用itertools.product + zip来获取此信息:

allzeniths, allazimuths = zip(*itertools.product(zeniths, azimuths))

然后你可以使用map:

amr = dview.map(f, allzeniths, allazimuths)

为了更深入地了解这些步骤,这里有一个例子:

zeniths = range(1,4)
azimuths = range(6,8)

product = list(itertools.product(zeniths, azimuths))
# [(1, 6), (1, 7), (2, 6), (2, 7), (3, 6), (3, 7)]

所以我们有一个“对列表”,但我们真正想要的是每个参数的单个列表,即“一对列表”。这正是稍微奇怪的zip(*product)语法让我们:

allzeniths, allazimuths = zip(*itertools.product(zeniths, azimuths))

print allzeniths
# (1, 1, 2, 2, 3, 3)
print allazimuths
# (6, 7, 6, 7, 6, 7)

现在我们只将我们的函数映射到这两个列表,以并行化嵌套for循环:

def f(z,a):
    return z*a

view.map(f, allzeniths, allazimuths)

没有什么特别的,只有两个 - 这个方法应该扩展到任意数量的嵌套循环。

答案 1 :(得分:9)

我假设您使用的是IPython 0.11或更高版本。首先定义一个简单的函数。

def foo(azimuth, zenith):
    # Do various bits of stuff
    # Eventually get a result
    return result

然后使用IPython的优秀并行套件来并行化您的问题。首先通过在终端窗口中启动集群来启动连接了5个引擎的控制器(#CPU + 1)(如果安装了IPython 0.11或更高版本,则该程序应该存在):

ipcluster start -n 5

在您的脚本中连接到控制器并传输您的所有任务。控制器会处理所有事情。

from IPython.parallel import Client

c = Client()   # here is where the client establishes the connection
lv = c.load_balanced_view()   # this object represents the engines (workers)

tasks = []
for azimuth in azimuths:
    for zenith in zeniths:
        tasks.append(lv.apply(foo, azimuth, zenith))

result = [task.get() for task in tasks]  # blocks until all results are back

答案 2 :(得分:1)

我对IPython并不熟悉,但一个简单的解决方案似乎只是将外循环并行化。

def f(azimuth):
    results = []
    for zenith in zeniths:
        #compute result
        results.append(result)
    return results

allresults = map(f, azimuths)

答案 3 :(得分:1)

如果您确实想要并行运行代码,请使用concurrent.futures

import itertools
import concurrent.futures

def _work_horse(azimuth, zenith):
    #DO HEAVY WORK HERE
    return result

futures = []
with concurrent.futures.ProcessPoolExecutor() as executor:
    for arg_set in itertools.product(zeniths, azimuths):
        futures.append(executor.submit(_work_horse, *arg_set))
executor.shutdown(wait=True)

# Will time out after one hour.
results = [future.result(3600) for future in futures]

答案 4 :(得分:1)

如果要保留循环的结构,可以尝试使用Ray(docs),它是用于编写并行和分布式Python的框架。一个要求是,您必须将可以并行化为其自身功能的工作分开。

您可以这样导入Ray:

setTimeout(function(){
    clickButton(buttons, i)
}, 2000); 

然后,您的脚本将如下所示:

import ray

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