当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《为什么在不指定关键字start时枚举执行速度较慢?》经验,为你挑选了1个好方法。

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)

这些以及拥有CALL_FUNCTION1个关键字是唯一的区别.

我尝试跟踪CPythons中的调用ceval,gdb并且似乎都使用了do_call,call_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.


这是我被困住的地方,无法弄清楚在哪里看.这可能只是在第二种情况下累积额外调用的开销,但同样,我不太确定.有谁知道这可能是什么原因?



1> Kasramvd..:

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

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

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

/* 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.


不,尝试将`start`指定为位置参数,速度差异消失.我会说关键字参数dict是罪魁祸首.
推荐阅读
大大炮
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有