【发布时间】:2013-07-17 14:25:44
【问题描述】:
我希望优化一些由两个嵌套循环组成的 python 代码。我对 numpy 不是很熟悉,但我知道它应该使我能够提高此类任务的效率。下面是我编写的测试代码,它反映了实际代码中发生的情况。目前使用 numpy 范围和迭代器比通常的 python 慢。我究竟做错了什么?这个问题的最佳解决方案是什么?
感谢您的帮助!
import numpy
import time
# setup a problem analagous to that in the real code
npoints_per_plane = 1000
nplanes = 64
naxis = 1000
npoints3d = naxis + npoints_per_plane * nplanes
npoints = naxis + npoints_per_plane
specres = 1000
# this is where the data is being mapped to
sol = dict()
sol["ems"] = numpy.zeros(npoints3d)
sol["abs"] = numpy.zeros(npoints3d)
# this would normally be non-random input data
data = dict()
data["ems"] = numpy.zeros((npoints,specres))
data["abs"] = numpy.zeros((npoints,specres))
for ip in range(npoints):
data["ems"][ip,:] = numpy.random.random(specres)[:]
data["abs"][ip,:] = numpy.random.random(specres)[:]
ems_mod = numpy.random.random(1)[0]
abs_mod = numpy.random.random(1)[0]
ispec = numpy.random.randint(specres)
# this the code I want to optimize
t0 = time.time()
# usual python range and iterator
for ip in range(npoints_per_plane):
jp = naxis + ip
for ipl in range(nplanes):
ip3d = jp + npoints_per_plane * ipl
sol["ems"][ip3d] = data["ems"][jp,ispec] * ems_mod
sol["abs"][ip3d] = data["abs"][jp,ispec] * abs_mod
t1 = time.time()
# numpy ranges and iterator
ip_vals = numpy.arange(npoints_per_plane)
ipl_vals = numpy.arange(nplanes)
for ip in numpy.nditer(ip_vals):
jp = naxis + ip
for ipl in numpy.nditer(ipl_vals):
ip3d = jp + npoints_per_plane * ipl
sol["ems"][ip3d] = data["ems"][jp,ispec] * ems_mod
sol["abs"][ip3d] = data["abs"][jp,ispec] * abs_mod
t2 = time.time()
print "plain python: %0.3f seconds" % ( t1 - t0 )
print "numpy: %0.3f seconds" % ( t2 - t1 )
编辑:仅将“jp = naxis + ip”放在第一个 for 循环中
补充说明:
我想出了如何让 numpy 快速执行内部循环,而不是外部循环:
# numpy vectorization
for ip in xrange(npoints_per_plane):
jp = naxis + ip
sol["ems"][jp:jp+npoints_per_plane*nplanes:npoints_per_plane] = data["ems"][jp,ispec] * ems_mod
sol["abs"][jp:jp+npoints_per_plane*nplanes:npoints_per_plane] = data["abs"][jp,ispec] * abs_mod
以下 Joe 的解决方案展示了如何同时进行这两种操作,谢谢!
【问题讨论】:
-
我不熟悉
numpy.range,但使用Python的range与创建n元素列表相同,而xrange是数字生成器——避免存储整个列表。 -
谢谢。你是对的,但是当我使用 xrange 而不是 range 时,运行时没有变化。
-
编写快速 numpy 代码的关键是避免通过矢量化操作(或者,更确切地说,将循环推低到快速 C 级别)循环,而不是迭代 numpy 对象是比较快的。 IOW,惯用的 numpy 代码没有很多
for循环,而是作为一个整体作用于向量和数组。 -
2 个问题。 1) 数组的大小是否与您的真实应用程序相同? 2) 你需要多快的代码运行速度?
-
1) 对于一个小问题,是的。对于最大的问题,它更像是 npoints_per_plane = 50000,nplanes = 64,naxis = 1000,specres = 1000。2)我希望代码以与 c 相似的数量级运行。我刚刚花了一个星期将一个 swig 接口转换为纯 python,但现在一个小问题需要两倍的时间,这就是瓶颈。