怎么把这个圈塞进纽比?

2024-05-14 05:32:50 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个慢循环,我想把它推到numpy中(快得多)。我已经花了好几天时间在玩这个代码,却一无所获。有没有可能,或者是我错过了一个新的把戏?我能做些重构吗?在

如您所见,我想将mixin加总为x的移位。在

import numpy as np

blocksize = 1000 # Chosen at runtime.
mixinsize = 100 # Chosen at runtime.
count = 10000 # Chosen at runtime.
xs = np.random.randint(0, blocksize + 1, count) # In practice this is data.
mixins = np.empty((count, mixinsize)) # In practice this is data.

# The slow part:
accumulator = np.zeros(blocksize + mixinsize)
for i in xrange(count):
    accumulator[xs[i]:xs[i] + mixinsize] += mixins[i]

对接受答案的评论

  • 为了让numba正常工作,我首先必须确保在任何地方都使用适当的numpy类型,当然不是普通的python类型。这本身就是一个巨大的性能改进。在
  • 当count变大时,Numba改进了速度的具体情况,这正是我想要的(也许我应该在问题中强调这一点)。在
  • 在我当前的测试用例中,不同的numba版本有明显不同的时间安排,但我认为numba是一个很好的想法,有很多潜力,而且它正在积极开发中,所以我会坚持下去。在
  • 使用水蟒来安装numba,特别是它可以很容易地在numba版本之间切换。在

Tags: innumpyiscountnp时间thisat
1条回答
网友
1楼 · 发布于 2024-05-14 05:32:50

从中获得0.11(还不是0.12)numba.pydata.org. 现在我们可以使用LLVM jit编译以下代码:

# plain NumPy version
import numpy as np

def foobar(mixinsize, count, xs, mixins, acc):
    for i in xrange(count):
        k = xs[i]
        acc[k:k + mixinsize] += mixins[i,:]


# LLVM compiled version
from numba import jit, void, int64, double
signature = void(int64,int64,int64[:],double[:,:],double[:])
foobar_jit = jit(signature)(foobar)

^{pr2}$
$ python test_numba.py
elapsed time: 590.632 ms
elapsed time with numba jit: 12.31 ms
speedup factor: 47.9799

好的,这几乎是50倍的加速,只需要额外的三行Python代码。在

现在我们还可以使用clang/LLVM作为编译器测试一个纯C版本进行比较。在


void foobar(long mixinsize, long count, 
    long *xs, double *mixins, double *accumulator)
{
    long i, j, k;
    double *cur, *acc;   
    for (i=0;i<count;i++) {
        acc = accumulator + xs[i];
        cur = mixins + i*mixinsize;
        for(j=0;j<mixinsize;j++) *acc++ += *cur++;
    }
}

from numpy.ctypeslib import ndpointer
import ctypes
so = ctypes.CDLL('plainc.so')
foobar_c = so.foobar
foobar_c.restype = None
foobar_c.argtypes = (
    ctypes.c_long,
    ctypes.c_long,
    ndpointer(dtype=np.int64, ndim=1),
    ndpointer(dtype=np.float64, ndim=2),
    ndpointer(dtype=np.float64, ndim=1)
)


t4 = clock()
foobar_c(mixinsize, count, xs, mixins, acc)
t5 = clock()
print("elapsed time with plain C: %g ms" % (1000*(t5-t4),))

$ CC -Ofast -shared -m64 -o plainc.so plainc.c
$ python test_numba.py
elapsed time: 599.136 ms
elapsed time with numba jit: 11.958 ms
speedup factor: 50.1034
elapsed time with plain C: 5.472 ms

因此,使用-Ofast进行优化时,Numba的速度大约是普通C版本的一半。相比之下,使用-O2的运行时大约为8毫秒,这意味着在本例中numbajit编译的Python的速度大约是带有-O2优化标志的C的75%。这对于另外三行Python代码来说并不坏。在

我们可以看一个简单的Python版本进行比较:

def foobar_py(mixinsize, count, xs, mixins, acc):
    for i in xrange(count):
        k = xs[i]
        for j in xrange(mixinsize):
            acc[j+k] += mixins[i][j]


# covert NumPy arrays to lists
_xs = map(int,xs)
_mixins = [map(float,mixins[i,:]) for i in xrange(count)]
_acc = map(float,acc)


t6 = clock()
foobar_py(mixinsize, count, _xs, _mixins, _acc)
t7 = clock()
print("elapsed time with plain Python: %g ms" % (1000*(t7-t6),))

这段Python代码在1775毫秒内执行。因此,相对于普通Python,使用NumPy可以获得大约3倍的加速,使用Numba可以获得150倍的加速,使用C和-Ofast可以得到350x的加速。在

Donald Knuth的一句警告他将此归因于C.A.R.Hoare:“过早优化是计算机编程中所有邪恶的根源。”虽然这似乎是令人印象深刻的相对加速,但沿着这条路线前进的绝对加速只允许我们节省一些毫秒的CPU时间。我的时间真的值得我花时间从那么多的劳动中节省CPU吗?你的时间值得吗?你自己决定。在

相关问题 更多 >