您好, 欢迎来到 !    登录 | 注册 | | 设为首页 | 收藏本站

python – 为什么__setitem__比cdef-classes的等效“普通”方法快得多?

5b51 2022/1/14 8:21:30 python 字数 6956 阅读 516 来源 www.jb51.cc/python

看起来,对于Cython的cdef类,使用类特殊方法有时比相同的“常规”方法更快,例如__setitem__比setitem快3倍:%%cython cdef class CyA: def __setitem__(self, index, val): pass def setitem(self, index, val):

概述

看起来,对于Cython的cdef类,使用类特殊方法有时比相同的“常规”方法更快,例如__setitem__比setitem快3倍:

%%cython
cdef class CyA:
    def __setitem__(self,index,val):
        pass
    def setitem(self,val):
        pass

现在:

cy_a=CyA()
%timeit cy_a[0]=3              # 32.4 ns ± 0.195 ns per loop (mean ± std. dev. of 7 runs,10000000 loops each)
%timeit cy_a.setitem(0,3)      # 97.5 ns ± 0.389 ns per loop (mean ± std. dev. of 7 runs,10000000 loops each)

这既不是Python的“正常”行为,特殊功能甚至更慢(并且比Cython等效的速度慢):

class PyA:
    def __setitem__(self,val):
        pass

py_a=PyA()
%timeit py_a[0]=3           # 198 ns ± 2.51 ns per loop (mean ± std. dev. of 7 runs,1000000 loops each)
%timeit py_a.setitem(0,3)   # 123 ns ± 0.619 ns per loop (mean ± std. dev. of 7 runs,10000000 loops each)

在Cython中,所有特殊功能都不是这样的:

%%cython
cdef class CyA:
    ...
    def __len__(self):
        return 1
    def len(self):
        return 1

这导致:

cy_a=CyA()
%timeit len(cy_a)    #  59.6 ns ± 0.233 ns per loop (mean ± std. dev. of 7 runs,10000000 loops each)
%timeit cy_a.len()   #  66.5 ns ± 0.326 ns per loop (mean ± std. dev. of 7 runs,10000000 loops each)

即几乎相同的运行时间.

为什么__setitem __(…)比cdef-class中的setitem(…)快得多,即使两者都是cython化的?

但是,可以优化C/C++ython类的特殊方法,如下所示:

查找速度

作为捷径,
Python C API中的PyTypeObject定义了许多不同的“槽” – 特殊方法的直接函数指针.对于__setitem__,实际上有两个可用:PyMappingMethods.mp_ass_subscript对应于通用的“映射”调用,而PySequenceMethods.sq_ass_item,它允许您直接使用int作为索引器并对应于C API函数PySequence_SetItem.

对于cdef类,Cython似乎只生成一个(通用)类,因此加速不是直接传递C int.生成非cdef类时,Cython不会填充这些插槽.

这些的优点是(对于C/C++ython类)找到__setitem__ function just involves a couple of pointer NULL checks followed by a C function call.这也适用于__len__,它也由PyTypeObject中的槽定义

相反,

>对于调用__setitem__的Python类,它改为uses a default implementation,它对字符串“__setitem__”进行字典查找.
>对于调用非特殊def函数的cdef或Python类,从类/实例字典中查找该属性(速度较慢)

请注意,如果setitem常规函数要在cdef类中定义为cpdef(并从Cython中调用),那么Cython会实现自己的机制以便快速查找.

呼唤效率

找到属性后必须调用它.在从PyTypeObject中检索特殊函数的地方(例如cdef类中的__setitem__和__len__),它们只是C函数指针,因此可以直接调用.

对于每个其他情况,必须对从属性查找中检索的PyObject进行求值,以查看它是否可调用,然后调用.

退货处理

当从PyTypeObject作为特殊函数调用__setitem__时,返回值是一个int,它只是用作错误标志.不需要引用计数或处理Python对象.

当从PyTypeObject作为特殊函数调用__len__时,返回类型是Py_ssize_t,必须将其转换为Python对象,然后在不再需要时销毁.

对于普通函数(例如,从Python或Cython类调用的setitem,或Python类中定义的__setitem__),返回值是PyObject *,必须对其进行适当的引用计数/销毁.

总之,差异实际上与查找和调用函数的快捷方式有关,而不是函数内容是否是Cython化.

总结

以上是编程之家为你收集整理的python – 为什么__setitem__比cdef-classes的等效“普通”方法快得多?全部内容,希望文章能够帮你解决python – 为什么__setitem__比cdef-classes的等效“普通”方法快得多?所遇到的程序开发问题。


如果您也喜欢它,动动您的小指点个赞吧

除非注明,文章均由 laddyq.com 整理发布,欢迎转载。

转载请注明:
链接:http://laddyq.com
来源:laddyq.com
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


联系我
置顶