返回语句时速度慢

3 投票
1 回答
1612 浏览
提问于 2025-04-17 15:15

我有一段Cython代码,主要是用来测试的:

cimport cython

cpdef loop(int k):
    return real_loop(k)

@cython.cdivision
cdef real_loop(int k):
    cdef int i
    cdef float a
    for i in xrange(k):
        a = i
        a = a**2 / (a + 1)
    return a

然后我用一个脚本来测试这段Cython代码和同样的纯Python代码之间的速度差异:

import mymodule

print(mymodule.loop(100000))

结果我发现Cython代码快了80倍。但是如果我把Cython代码中的两个返回语句去掉,速度竟然快了800到900倍。这是为什么呢?

还有,如果我在我的老款ACER Aspire ONE笔记本上运行这段代码(带返回语句),速度是700倍,而在家里的新款i7台式机上,速度只有80倍。

有人知道这是为什么吗?

1 个回答

0

我用以下代码测试了你的问题:

#cython: wraparound=False
#cython: boundscheck=False
#cython: cdivision=True
#cython: nonecheck=False
#cython: profile=False

def loop(int k):
 return real_loop(k)

def loop2(int k):
 cdef float a
 real_loop2(k, &a)
 return a

def loop3(int k):
    real_loop3(k)
    return None

def loop4(int k):
    return real_loop4(k)

def loop5(int k):
 cdef float a
 real_loop5(k, &a)
 return a

cdef float real_loop(int k):
    cdef int i
    cdef float a
    a = 0.
    for i in range(k):
        a += a**2 / (a + 1)
    return a

cdef void real_loop2(int k, float *a):
    cdef int i
    a[0] = 0.
    for i in range(k):
        a[0] += a[0]**2 / (a[0] + 1)

cdef void real_loop3(int k):
    cdef int i
    cdef float a
    a = 0.
    for i in range(k):
        a += a**2 / (a + 1)

cdef float real_loop4(int k):
    cdef int i
    cdef float a
    a = 0.
    for i in range(k):
        a += a*a / (a + 1)
    return a

cdef void real_loop5(int k, float *a):
    cdef int i
    a[0] = 0.
    for i in range(k):
        a[0] += a[0]*a[0] / (a[0] + 1)

这里的 real_loop() 函数和你的函数很接近,只是对 a 的公式做了些修改,因为原来的公式看起来有点奇怪。

函数 real_loop2() 不返回任何值,只是通过引用更新了 a

函数 real_loop3() 也不返回任何值。

查看生成的 C 代码,可以看到 real_loop3() 中有循环,并且代码被调用了……但我和 @dmytro 的结论一样,改变 k 不会显著改变时间……所以这里一定有我遗漏的地方。

从下面的时间测试结果来看,我们可以说 return 不是瓶颈,因为 real_loop2()real_loop5() 都没有返回任何值,它们的性能和 real_loop() 以及 real_loop4() 是一样的。

In [2]: timeit _stack.loop(100000)
1000 loops, best of 3: 1.71 ms per loop

In [3]: timeit _stack.loop2(100000)
1000 loops, best of 3: 1.69 ms per loop

In [4]: timeit _stack.loop3(100000)
10000000 loops, best of 3: 78.5 ns per loop

In [5]: timeit _stack.loop4(100000)
1000 loops, best of 3: 913 µs per loop

In [6]: timeit _stack.loop5(100000)
1000 loops, best of 3: 979 µs per loop

注意到将 a**2 改为 a*a 后速度提升了大约2倍,因为 a**2 在循环中需要调用一个函数 powf()

撰写回答