gpt4 book ai didi

python - 为什么在不指定关键字start时枚举执行速度较慢?

转载 作者:太空狗 更新时间:2023-10-29 18:32:26 26 4
gpt4 key购买 nike

我在计时 enumerate 时注意到以下奇怪行为使用指定的默认 start 参数:

In [23]: %timeit enumerate([1, 2, 3, 4])
The slowest run took 7.18 times longer than the fastest. This could mean that an intermediate result is being cached
1000000 loops, best of 3: 511 ns per loop

In [24]: %timeit enumerate([1, 2, 3, 4], start=0)
The slowest run took 12.45 times longer than the fastest. This could mean that an intermediate result is being cached
1000000 loops, best of 3: 1.22 µs per loop

因此,对于指定 start 的情况,速度大约降低 2 倍。

为每种情况发布的字节代码并没有真正表明任何会导致速度显着差异的内容。一个恰当的例子,在用 dis.dis 检查了不同的调用之后发出的附加命令是:

18 LOAD_CONST               5 ('start')
21 LOAD_CONST 6 (0)

这些以及具有 1 个关键字的 CALL_FUNCTION 是唯一的区别。

我尝试跟踪 CPython 中的调用 ceval使用 gdb 并且两者似乎都使用 do_callcall_function 中,而不是我能检测到的其他优化。

现在,我明白 enumerate 只是创建一个枚举迭代器,所以我们在这里处理对象创建(对吧?)。我查看了 Objects/enumobject.c 如果指定了 start,则尝试发现任何差异。唯一(我相信)不同的是当 start != NULL 时会发生以下情况:

if (start != NULL) {
start = PyNumber_Index(start);
if (start == NULL) {
Py_DECREF(en);
return NULL;
}
assert(PyInt_Check(start) || PyLong_Check(start));
en->en_index = PyInt_AsSsize_t(start);
if (en->en_index == -1 && PyErr_Occurred()) {
PyErr_Clear();
en->en_index = PY_SSIZE_T_MAX;
en->en_longindex = start;
} else {
en->en_longindex = NULL;
Py_DECREF(start);
}

这看起来不像是会引入 2 倍减速的东西。 (我想,不确定。)

前面的代码段已在 Python 3.5 上执行,不过在 2.x 中也有类似的结果。


这是我被困住的地方,不知道该往哪里看。这可能只是第二种情况下额外调用累积的开销的情况,但同样,我不太确定。 有人知道这背后的原因是什么吗?

最佳答案

一个原因可能是调用了 PyNumber_Index当您在以下部分指定开始时:

if (start != NULL) {
start = PyNumber_Index(start);

如果你看一下 abstract.c 中的 PyNumber_Index 函数模块,您将在函数的顶层看到以下注释:

/* Return a Python int from the object item.
Raise TypeError if the result is not an int
or if the object cannot be interpreted as an index.
*/

所以这个函数必须检查对象是否不能被解释为索引并返回相关错误。如果您仔细查看源代码,您会看到所有这些检查和引用,特别是在以下部分,它必须执行嵌套结构取消引用以检查索引类型:

result = item->ob_type->tp_as_number->nb_index(item);
if (result &&
!PyInt_Check(result) && !PyLong_Check(result)) {
...

检查并返回所需结果会花费很多时间。


但正如@user2357112 提到的,另一个也是最重要的原因是因为 python 关键字参数匹配。

如果您对不带关键字参数的函数进行计时,您会看到差异时间将减少大约 2 倍时间:

~$ python -m timeit "enumerate([1, 2, 3, 4])"
1000000 loops, best of 3: 0.251 usec per loop
~$ python -m timeit "enumerate([1, 2, 3, 4],start=0)"
1000000 loops, best of 3: 0.431 usec per loop
~$ python -m timeit "enumerate([1, 2, 3, 4],0)"
1000000 loops, best of 3: 0.275 usec per loop

与位置参数的区别在于:

>>> 0.251 - 0.275
-0.024

这似乎是因为 PyNumber_Index

关于python - 为什么在不指定关键字start时枚举执行速度较慢?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34676913/

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