【问题标题】:Slow speed when return statementreturn 语句速度慢
【发布时间】:2013-01-24 09:13:25
【问题描述】:

我有这个 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))

我的速度提高了 80 倍。但是如果我删除 cython 代码中的两个 return 语句,我会快 800-900 倍。为什么 ?

另一件事是,如果我在旧的 ACER Aspire ONE 笔记本上运行此代码(返回),我的速度会快 700 倍,而在家里的新台式 i7 PC 上,速度会快 80 倍。

有人知道为什么吗?

【问题讨论】:

  • 很难说——我想知道编译器是否足够聪明,可以看到 real_loop 不会更新任何全局变量,也不会更改任何参数,所以它可以变成无操作。
  • 你试过diff这两个生成的C文件看看有什么不同吗?无论如何,也许是因为 cython 看到没有return 的函数是没用的,并且根本循环。
  • @Bakuriu 是的,这就是我的想法。可能根本不做循环。我不知道怎么做,但昨天当我尝试不同的编码方式时,我得到了一些荒谬的性能,比如快了 12 000 倍。
  • 这太过分了。这意味着编译器将整个循环替换为单个无操作。如果所用时间不随输入大小而变化,您应该会看到这一点。
  • 您可能在这些系统上有不同的编译器。在我的装有 gcc4.7.2 的 Corei5 笔记本电脑上,mymodule.loop(N) 总是需要大约 120 纳秒,无论 N 有多大,即它会跳过循环。

标签: python performance return cython


【解决方案1】:

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

#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*aa**2 更改为 ~2X 加速,因为 a**2 需要在循环内调用函数 powf()

【讨论】:

    猜你喜欢
    • 2021-10-09
    • 2015-03-22
    • 2011-11-13
    • 1970-01-01
    • 1970-01-01
    • 2016-12-29
    • 2017-02-05
    • 2016-10-16
    相关资源
    最近更新 更多