- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
我想测试cython
与标准 python 相比的性能。所以这里我有 3 个函数示例,它将循环遍历 200 个整数,一遍又一遍地将相同的数字添加到结果中,然后返回结果。在 timeit
我将其命名为 1.000.000
模块次。
这是第一个例子:
[frynio@manjaro ctest]$ cat nocdefexample.pyx
def nocdef(int num):
cdef int result = 0
for i in range(num):
result += num
return result
def xd(int num):
return nocdef(num)
这是第二个(仔细看,第一个函数定义很重要):
[frynio@manjaro ctest]$ cat cdefexample.pyx
cdef int cdefex(int num):
cdef int result = 0
for i in range(num):
result += num
return result
def xd1(int num):
return cdefex(num)
还有第三个,它位于主文件中:
[frynio@manjaro ctest]$ cat test.py
from nocdefexample import xd
from cdefexample import xd1
import timeit
def standardpython(num):
result = 0
for i in range(num):
result += num
return result
def xd2(num):
return standardpython(num)
print(timeit.timeit('xd(200)', setup='from nocdefexample import xd', number=1000000))
print(timeit.timeit('xd1(200)', setup='from cdefexample import xd1', number=1000000))
print(timeit.timeit('xd2(200)', setup='from __main__ import xd2', number=1000000))
我用 cythonize -a -i nocdefexample.pyx cdefexample.pyx
编译它我有两个.so
s。然后当我运行python test.py
时- 这显示:
[frynio@manjaro ctest]$ python test.py
0.10323301900007209
0.06339033499989455
11.448068103000423
所以第一个只有def <name>(int num)
。第二个(似乎比第一个更快 1.5x
)是 cdef int <name>(int num)
。最后一个就是def <name>(num)
.
最后的表现很糟糕,但这就是我想看到的。对我来说有趣的是为什么前两个例子不同(我检查了很多次,第二个总是 ~ 1.5x
比第一个更快)。
只是因为我指定了返回类型吗?
如果是这样,是否意味着它们都是 cython
函数还是第一个,我不知道,混合类型的函数?
最佳答案
首先,您必须意识到,在 cython 函数的情况下,您仅测量调用 cdef
的开销- 与a def
-功能:
>>> %timeit nocdef(1000)
60.5 ns ± 0.73 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
>>> %timeit nocdef(10000)
60.1 ns ± 1.2 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
C 编译器认识到循环将导致 num*num
并直接计算该乘法而不运行循环 - 并且对于 10**3
来说乘法同样快和10**4
.
这对于 python 程序员来说可能会感到惊讶,因为 python 解释器不会优化,因此这个循环有一个 O(n)
-运行时间:
>>> %timeit standardpython(1000)
43.7 µs ± 182 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
>>> %timeit standardpython(10000)
479 µs ± 4.95 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
现在,调用cdef
功能更快!只需查看生成的用于调用 cdef
的 C 代码即可。版本(实际上已经合并了 python-integer 的创建):
__pyx_t_1 = __Pyx_PyInt_From_int(__pyx_f_4test_cdefex(__pyx_v_num)); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 19, __pyx_L1_error)
__pyx_f_4test_cdefex
- 只是 C 函数的调用。与 def
的调用相比-通过整个 python 机器发生的版本(这里有点缩写):
...
__pyx_t_2 = __Pyx_GetModuleGlobalName(__pyx_n_s_nocdef); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 9, __pyx_L1_error)
...
__pyx_t_3 = __Pyx_PyInt_From_int(__pyx_v_num); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 9, __pyx_L1_error)
...
__pyx_t_4 = PyMethod_GET_SELF(__pyx_t_2);
...
__pyx_t_1 = __Pyx_PyObject_CallOneArg(__pyx_t_2, __pyx_t_3); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 9, __pyx_L1_error)
Cython 必须:
num
能够调用 Python 函数 ( __Pyx_PyInt_From_int
)__Pyx_GetModuleGlobalName
+ PyMethod_GET_SELF
)第一次调用可能至少快了 100 倍,但总体加速率小于 2,只是因为调用“内部”函数并不是唯一需要完成的工作:def
-功能xd
和xd1
无论如何都必须调用+必须创建生成的python-integer。
有趣的事实:
>>> %timeit nocdef(16)
44.1 ns ± 0.294 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
>>> %timeit nocdef(17)
58.5 ns ± 0.638 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
原因是integer pool对于值 -5
... 256
=16^2
因此可以更快地构建此范围内的值。
指定返回类型在您的示例中并没有发挥那么大的作用:它只决定发生到 python-integer 的转换的位置 - 或者在 nocdef
中或xd1
- 但它最终会发生。
关于python - 3 个 cython/python 函数调用之间的差异,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48854878/
C语言sscanf()函数:从字符串中读取指定格式的数据 头文件: ?
最近,我有一个关于工作预评估的问题,即使查询了每个功能的工作原理,我也不知道如何解决。这是一个伪代码。 下面是一个名为foo()的函数,该函数将被传递一个值并返回一个值。如果将以下值传递给foo函数,
CStr 函数 返回表达式,该表达式已被转换为 String 子类型的 Variant。 CStr(expression) expression 参数是任意有效的表达式。 说明 通常,可以
CSng 函数 返回表达式,该表达式已被转换为 Single 子类型的 Variant。 CSng(expression) expression 参数是任意有效的表达式。 说明 通常,可
CreateObject 函数 创建并返回对 Automation 对象的引用。 CreateObject(servername.typename [, location]) 参数 serv
Cos 函数 返回某个角的余弦值。 Cos(number) number 参数可以是任何将某个角表示为弧度的有效数值表达式。 说明 Cos 函数取某个角并返回直角三角形两边的比值。此比值是
CLng 函数 返回表达式,此表达式已被转换为 Long 子类型的 Variant。 CLng(expression) expression 参数是任意有效的表达式。 说明 通常,您可以使
CInt 函数 返回表达式,此表达式已被转换为 Integer 子类型的 Variant。 CInt(expression) expression 参数是任意有效的表达式。 说明 通常,可
Chr 函数 返回与指定的 ANSI 字符代码相对应的字符。 Chr(charcode) charcode 参数是可以标识字符的数字。 说明 从 0 到 31 的数字表示标准的不可打印的
CDbl 函数 返回表达式,此表达式已被转换为 Double 子类型的 Variant。 CDbl(expression) expression 参数是任意有效的表达式。 说明 通常,您可
CDate 函数 返回表达式,此表达式已被转换为 Date 子类型的 Variant。 CDate(date) date 参数是任意有效的日期表达式。 说明 IsDate 函数用于判断 d
CCur 函数 返回表达式,此表达式已被转换为 Currency 子类型的 Variant。 CCur(expression) expression 参数是任意有效的表达式。 说明 通常,
CByte 函数 返回表达式,此表达式已被转换为 Byte 子类型的 Variant。 CByte(expression) expression 参数是任意有效的表达式。 说明 通常,可以
CBool 函数 返回表达式,此表达式已转换为 Boolean 子类型的 Variant。 CBool(expression) expression 是任意有效的表达式。 说明 如果 ex
Atn 函数 返回数值的反正切值。 Atn(number) number 参数可以是任意有效的数值表达式。 说明 Atn 函数计算直角三角形两个边的比值 (number) 并返回对应角的弧
Asc 函数 返回与字符串的第一个字母对应的 ANSI 字符代码。 Asc(string) string 参数是任意有效的字符串表达式。如果 string 参数未包含字符,则将发生运行时错误。
Array 函数 返回包含数组的 Variant。 Array(arglist) arglist 参数是赋给包含在 Variant 中的数组元素的值的列表(用逗号分隔)。如果没有指定此参数,则
Abs 函数 返回数字的绝对值。 Abs(number) number 参数可以是任意有效的数值表达式。如果 number 包含 Null,则返回 Null;如果是未初始化变量,则返回 0。
FormatPercent 函数 返回表达式,此表达式已被格式化为尾随有 % 符号的百分比(乘以 100 )。 FormatPercent(expression[,NumDigitsAfterD
FormatNumber 函数 返回表达式,此表达式已被格式化为数值。 FormatNumber( expression [,NumDigitsAfterDecimal [,Inc
我是一名优秀的程序员,十分优秀!