如果我在Python中有一个生成器函数,请说:
def gen(x):
for i in range(x):
yield(i ** 2)
Run Code Online (Sandbox Code Playgroud)
如何声明输出数据类型是否int在Cython中?它是否值得一试?
谢谢.
编辑:我读过在更改日志中实现的(异步)生成器的提及:http://cython.readthedocs.io/en/latest/src/changes.html? highlight = generator#id23
但是没有关于如何使用它们的文档.是因为它们受到支持,但使用Cython或没有可能的优化没有特别的优势吗?
不,在Cython中没有办法做到这一点.
当您查看Cython生成的代码时,您将看到gen(和其他生成器函数)返回一个生成器,它基本上是一个__pyx_CoroutineObject对象,如下所示:
typedef PyObject *(*__pyx_coroutine_body_t)(PyObject *, PyThreadState *, PyObject *);
typedef struct {
PyObject_HEAD
__pyx_coroutine_body_t body;
PyObject *closure;
...
int resume_label;
char is_running;
} __pyx_CoroutineObject;
Run Code Online (Sandbox Code Playgroud)
最重要的部分是body-member:这是进行实际计算的函数.正如我们可以看到它返回一个PyObject,也没有为它适应的方式(没有?) int,double或类似的.
至于为什么没有这样做的原因,我只能推测 - 但可能不止一个原因.
如果你真的关心性能,那么生成器无论如何都会引入太多的开销(例如,yield在cdef函数中是不可能的),并且应该重构为更简单的东西.
详细说明可能的重构.作为基线,我们假设我们想要总结所有创建的值:
%%cython
def gen(int x):
cdef int i
for i in range(x):
yield(i ** 2)
def sum_it(int n):
cdef int i
cdef int res=0
for i in gen(n):
res+=i
return res
Run Code Online (Sandbox Code Playgroud)
时间安排导致:
>>> %timeit sum_it(1000)
28.9 µs ± 1.06 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
Run Code Online (Sandbox Code Playgroud)
好消息:它比纯python版快10倍,但如果我们真的在速度之后:
%%cython
cdef int gen_fast(int i):
return i ** 2
def sum_it_fast(int n):
cdef int i
cdef int res=0
for i in range(n):
res+=gen_fast(i)
return res
Run Code Online (Sandbox Code Playgroud)
它是:
>>> %timeit sum_it_fast(1000)
661 ns ± 20.7 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
Run Code Online (Sandbox Code Playgroud)
大约快50倍.
我明白,这是一个相当大的改变,可能很难做到 - 只有当它真的是我的计划的瓶颈时才会这样做 - 但是加速50将是一个真正的动力去做.
显然还有很多其他方法:使用numpy-arrays或array.array代替生成器或编写自定义生成器(cdef-class),这将提供额外的快速/有效的可能性来获取int-values而不是PyObjects- 但这一切都取决于你的手头的情景.我只想表明有可能通过抛弃发电机来提高性能.
| 归档时间: |
|
| 查看次数: |
1149 次 |
| 最近记录: |