gpt4 book ai didi

python - 为什么 __setitem__ 比 cdef 类的等效 "normal"方法快得多?

转载 作者:太空狗 更新时间:2023-10-30 01:18:17 25 4
gpt4 key购买 nike

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

%%cython
cdef class CyA:
def __setitem__(self, index, val):
pass
def setitem(self, index, 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 的“正常”行为,因为 Python 的特殊函数甚至有点慢(显然比 Cython 等价物慢):

class PyA:
def __setitem__(self, index, val):
pass
def setitem(self, index, 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 类中的 setitem(...) 快得多,即使两者都是 cythonized?

最佳答案

通用 Python 方法调用有相当多的开销 - Python 查找相关属性(字典查找),确保该属性是可调用对象,并在调用后处理结果。此开销也适用于 cdef 类的通用 def 函数(唯一的区别是该方法的实现是在 C 中定义的)。

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

查找速度

作为捷径,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/Cython 类)找到 __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 类,从类/实例字典中查找属性(速度较慢)

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

调用效率

找到必须调用的属性。从 PyTypeObject(例如 cdef class 上的 __setitem____len__)检索特殊函数的地方,它们是简单的 C 函数指针,因此可以直接调用。

对于所有其他情况,必须评估从属性查找中检索到的 PyObject 以查看它是否可调用,然后调用。

返回处理

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

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

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


总而言之,差异实际上与查找和调用函数的快捷方式有关,而不是函数的内容是否经过 Cythonized。

关于python - 为什么 __setitem__ 比 cdef 类的等效 "normal"方法快得多?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53528716/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com