Python - 使用多进程进行matplotlib griddata处理

16

在我的前一个问题[1]之后,我想将多进程应用于matplotlib的griddata函数。是否可能将griddata分成4个部分,每个部分都由我的4个核心处理? 我需要这样做来提高性能。

例如,请尝试以下代码,并尝试使用不同的size值进行实验:

import numpy as np
import matplotlib.mlab as mlab
import time

size = 500

Y = np.arange(size)
X = np.arange(size)
x, y = np.meshgrid(X, Y)
u = x * np.sin(5) + y * np.cos(5)
v = x * np.cos(5) + y * np.sin(5)
test = x + y

tic = time.clock()

test_d = mlab.griddata(
    x.flatten(), y.flatten(), test.flatten(), x+u, y+v, interp='linear')

toc = time.clock()

print 'Time=', toc-tic

4
我认为你无法应用多进程。也许这个问题https://dev59.com/TVvUa4cB1Zd3GeqPr0np会有所帮助? - otterb
示例代码语法不正确。您打算如何处理以下行:test= xx+yy - OYRM
1
我修复了代码,现在应该可以运行了。 - Velimir Mlaker
感谢您的贡献 :) - user3601754
1个回答

5

我在Macbook Pro上运行了以下示例代码,使用的是Python 3.4.2版本,numpy版本为1.9.1,matplotlib版本为1.4.2。该电脑有4个物理CPU(与Mac硬件架构提供的一些用例中的“虚拟”CPU不同):

import numpy as np
import matplotlib.mlab as mlab
import time
import multiprocessing

# This value should be set much larger than nprocs, defined later below
size = 500

Y = np.arange(size)
X = np.arange(size)
x, y = np.meshgrid(X, Y)
u = x * np.sin(5) + y * np.cos(5)
v = x * np.cos(5) + y * np.sin(5)
test = x + y

tic = time.clock()

test_d = mlab.griddata(
    x.flatten(), y.flatten(), test.flatten(), x+u, y+v, interp='linear')

toc = time.clock()

print('Single Processor Time={0}'.format(toc-tic))

# Put interpolation points into a single array so that we can slice it easily
xi = x + u
yi = y + v
# My example test machine has 4 physical CPUs
nprocs = 4
jump = int(size/nprocs)

# Enclose the griddata function in a wrapper which will communicate its
# output result back to the calling process via a Queue
def wrapper(x, y, z, xi, yi, q):
    test_w = mlab.griddata(x, y, z, xi, yi, interp='linear')
    q.put(test_w)

# Measure the elapsed time for multiprocessing separately
ticm = time.clock()

queue, process = [], []
for n in range(nprocs):
    queue.append(multiprocessing.Queue())
    # Handle the possibility that size is not evenly divisible by nprocs
    if n == (nprocs-1):
        finalidx = size
    else:
        finalidx = (n + 1) * jump
    # Define the arguments, dividing the interpolation variables into
    # nprocs roughly evenly sized slices
    argtuple = (x.flatten(), y.flatten(), test.flatten(),
                xi[:,(n*jump):finalidx], yi[:,(n*jump):finalidx], queue[-1])
    # Create the processes, and launch them
    process.append(multiprocessing.Process(target=wrapper, args=argtuple))
    process[-1].start()

# Initialize an array to hold the return value, and make sure that it is
# null-valued but of the appropriate size
test_m = np.asarray([[] for s in range(size)])
# Read the individual results back from the queues and concatenate them
# into the return array
for q, p in zip(queue, process):
    test_m = np.concatenate((test_m, q.get()), axis=1)
    p.join()

tocm = time.clock()

print('Multiprocessing Time={0}'.format(tocm-ticm))

# Check that the result of both methods is actually the same; should raise
# an AssertionError exception if assertion is not True
assert np.all(test_d == test_m)

我得到了以下结果:

/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/site-packages/matplotlib/tri/triangulation.py:110: FutureWarning: comparison to `None` will result in an elementwise object comparison in the future.self._neighbors)
Single Processor Time=8.495998
Multiprocessing Time=2.249938

我不太确定是什么原因导致了triangulation.py的“未来警告”(显然,我的matplotlib版本不喜欢最初提供的问题输入值的某些内容),但无论如何,多进程似乎确实实现了期望的加速比8.50 / 2.25 = 3.8,(编辑:见评论),大约是我们预计4个CPU的机器可以达到的4倍左右。并且最后的断言语句也成功执行,证明两种方法得到相同的答案,因此尽管有稍微奇怪的警告信息,我认为上面的代码是一个有效的解决方案。


EDIT:一位评论者指出,我的解决方案以及原作者发布的代码片段都可能使用了错误的方法time.clock()来测量执行时间;他建议使用time.time()。我也开始认同他的观点。(深入研究Python文档后,我仍然不确定这个解决方案是否100%正确,因为新版本的Python似乎已经弃用了time.clock(),转而使用time.perf_counter()time.process_time()。但无论如何,我同意无论time.time()是否绝对正确地进行了这个测量,它仍然比我之前使用的time.clock()更正确。)
假设评论者的观点是正确的,则意味着我认为我所测量到的大约4倍加速是错误的。
然而,这并不意味着底层代码本身没有正确地并行化;相反,它只是意味着在这种情况下,并行化实际上并没有帮助;将数据分割并在多个处理器上运行并没有改进任何东西。为什么会这样?其他用户已经指出,至少在numpy/scipy中,一些函数可以在多个核心上运行,而另一些则不行,对于最终用户来说,试图弄清哪些函数是哪些函数是一个非常具有挑战性的研究项目。根据这个实验的结果,如果我的解决方案在Python中正确实现了并行化,但没有进一步的加速,那么我认为最简单的可能解释是matplotlib可能也在"幕后"以编译的C++库的形式并行化了一些其功能,就像numpy/scipy已经做的那样。假设是这样的话,那么这个问题的正确答案就是:如果底层的C++库已经悄悄地在多个内核上运行,那么在Python中进一步并行化也无济于事。

1
很不幸,您没有使用“time.clock()”计算墙钟时间(请参见https://dev59.com/mH_aa4cB1Zd3GeqPzCG9#23325328)。相反,请使用“time.time()”,并注意多进程场景实际上需要更长时间。虽然这是一个好尝试!我也曾经试过自己分割输入值,但“griddata()”没有任何速度提升。 :( - Velimir Mlaker
1
抱歉,@stachyra的答案是不正确的。将“time.clock()”替换为“time.time()”后,真正的挂钟性能更差。我的8个CPU机器给出的结果是:“单处理器时间=8.833,多处理器时间=11.677”。 - Velimir Mlaker
我无法启动它...我收到了一个错误:"Traceback (most recent call last): File "/usr/lib/python2.7/multiprocessing/process.py", line 258, in _bootstrap
self._target(*self._args, **self._kwargs) File "<ipython-input-2-9885522fee1f>", line 11, in wrapper test_w = mlab.griddata(x, y, z, xi, yi, interp='linear') File "/usr/lib/pymodules/python2.7/matplotlib/mlab.py", line 2619, in griddata raise ValueError("output grid must have constant spacing" ValueError: output grid must have constant spacing when using interp='linear'..."
- user3601754
1
@user3601754:你的matplotlib版本很可能已经过时了。正如我在回答开头所述,我在Python 3.4.2版本下运行了上面的代码,并使用了matplotlib 1.4.2版本。我还在同一测试机器上安装了一个较旧的Python 2.7.5版本,该版本使用matplotlib 1.1.1。当我尝试使用这些旧版本号运行我的解决方案代码时,我得到了与你相同的错误。尝试将matplotlib升级到最新版本,几乎肯定会解决这个问题。 - stachyra

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接