18

我编写了一个算法,该算法采用地理空间数据并执行多个步骤。输入数据是大型栅格研究区域(约 1.5 亿像素)的多边形和协变量栅格的 shapefile。步骤如下:

  1. shapefile 多边形内的采样点
  2. 对于每个采样点,从协变量栅格中提取值
  3. 在采样点上建立预测模型
  4. 提取目标网格点的协变量
  5. 将预测模型应用于目标网格
  6. 将预测写入一组输出网格

整个过程需要迭代多次(比如 100 次),但当前每次迭代需要一个多小时的时间进行串行处理。对于每次迭代,最耗时的部分是第 4 步和第 5 步。由于目标网格非常大,我一直在一次处理一个块(比如 1000 行)。

我有一个 6 核 CPU 和 32 Gb RAM,所以在每次迭代中,我尝试使用 Python 的multiprocessing模块和一个Pool对象同时处理多个块(步骤 4 和 5),然后写入输出(预测)使用调用全局输出写入函数的回调函数到公共输出网格集。这似乎可行,但并不比串行处理每个块更快(实际上,它可能更慢)。

所以我的问题是,有没有更有效的方法来做到这一点?我对多处理模块的Queue类感兴趣,但我不确定它是如何工作的。例如,我想知道让一个执行步骤 4 和 5 然后将结果传递到执行步骤 6 的另一个队列是否更有效。或者这甚至是 Queue 的用途吗?

任何指针将不胜感激。

4

4 回答 4

3

Python 的多处理能力的当前状态对于 CPU 绑定处理来说并不是很好。我害怕告诉您,使用该模块无法使其运行得更快,multiprocessing也不是您使用该模块multiprocessing的问题。

真正的问题是 Python 仍然受GlobalInterpreterLock(GIL)规则的约束(我强烈建议使用幻灯片)。围绕 GIL 开展工作取得了一些令人兴奋的理论和实验进展。Python 3.2 事件包含一个新的 GIL,它解决了一些问题,但引入了其他问题。

目前,使用单个串行线程执行多个 Python 进程比尝试在一个进程中运行多个线程要快。这将允许您避免在线程之间获取 GIL 的问题(通过有效地拥有更多 GIL)。然而,这只有在 Python 进程之间的 IPC 开销不超过处理的好处时才有用。

Eli Bendersky 写了一篇不错的概述文章,讲述了他尝试通过多处理使受 CPU 限制的进程运行得更快的经验。

值得注意的是,PEP 371希望通过引入multiprocessing模块(以前是一个名为 的非标准封装pyProcessing)来“回避” GIL。然而,GIL 似乎仍然在 Python 解释器中扮演了太大的角色,无法使其与 CPU 绑定算法一起工作。许多不同的人都致力于删除/重写 GIL,但没有足够的牵引力使其成为 Python 版本。

于 2012-06-20T14:24:49.880 回答
1

python.org 上的一些多处理示例不是很清楚 IMO,很容易从有缺陷的设计开始。这是我为开始一个项目而制作的一个简单示例:

import os, time, random, multiprocessing
def busyfunc(runseconds):
    starttime = int(time.clock())
    while 1:
        for randcount in range(0,100):
            testnum = random.randint(1, 10000000)
            newnum = testnum / 3.256
        newtime = int(time.clock())
        if newtime - starttime > runseconds:
            return

def main(arg):
    print 'arg from init:', arg
    print "I am " + multiprocessing.current_process().name

    busyfunc(15)

if __name__ == '__main__':

    p = multiprocessing.Process(name = "One", target=main, args=('passed_arg1',))
    p.start()

    p = multiprocessing.Process(name = "Two", target=main, args=('passed_arg2',))
    p.start()

    p = multiprocessing.Process(name = "Three", target=main, args=('passed_arg3',))
    p.start()

    time.sleep(5)

这应该使 3 个处理器运行 15 秒。修改它应该很容易。也许这将有助于调试您当前的代码并确保您真正生成多个独立进程。

如果由于 RAM 限制而必须共享数据,那么我建议这样做: http: //docs.python.org/library/multiprocessing.html#sharing-state-between-processes

于 2012-06-15T22:31:13.670 回答
1

由于 python 并不是真的要进行密集的数字计算,我通常会开始将 python 程序的时间关键部分转换为 C/C++ 并大大加快速度。

此外,python 多线程也不是很好。Python 一直在为各种事情使用全局信号量。因此,即使您使用 python 提供的线程,事情也不会变得更快。线程对于应用程序很有用,其中线程通常会等待诸如 IO 之类的事情。

在制作 C 模块时,您可以在处理数据时手动释放全局信号量(当然,不再访问 python 值)。

使用 C API 需要一些练习,但它的结构清晰且比 Java 原生 API 更容易使用。

请参阅 python 文档中的“扩展和嵌入”。

通过这种方式,您可以在 C/C++ 中制作时间关键部分,而在 python 中使用更快的编程工作的较慢部分......

于 2012-06-20T08:32:33.730 回答
0

我建议您首先检查代码的哪些方面花费的时间最多,因此您必须对其进行分析,尽管它成功地使用了http://packages.python.org/line_profiler/#line-profiler确实需要cython。

至于队列,它们主要用于共享数据/同步线程,尽管我很少使用它。我确实一直在使用多处理。

我主要遵循 map reduce 哲学,它简单而干净,但它有一些主要开销,因为在应用 map 函数时,值必须打包到字典中并在每个进程中复制......

您可以尝试分割您的文件并将您的算法应用于不同的集合。

于 2012-06-11T05:52:53.850 回答