【问题标题】:Why evaluating boolean objects takes time in python为什么在 python 中评估布尔对象需要时间
【发布时间】:2016-02-10 16:19:42
【问题描述】:

我使用timeit模块比较了这两个代码sn-ps,发现第二个稍快:

~$ python -m timeit —setup "l=[1, 2];k=1" "l[k==1]"
10000000 loops, best of 3: 0.0414 usec per loop
~$ python -m timeit —setup "l=[1, 2];k=1" "l[0 if k==1 else 1]"
10000000 loops, best of 3: 0.0372 usec per loop

由于逻辑相同,我认为评估布尔对象比整数等价(True == 1 和False == 0)花费更多时间,因此我提出了以下基准,结果证明我是正确的:

~$ python -m timeit —setup "l=range(1000)" "l[False]"
10000000 loops, best of 3: 0.0411 usec per loop
~$ python -m timeit —setup "l=range(1000)" "l[False]"
10000000 loops, best of 3: 0.0394 usec per loop
~$ python -m timeit —setup "l=range(1000)" "l[False]"
10000000 loops, best of 3: 0.0416 usec per loop
~$ python -m timeit —setup "l=range(1000)" "l[True]"
10000000 loops, best of 3: 0.0428 usec per loop
~$ python -m timeit —setup "l=range(1000)" "l[True]"
10000000 loops, best of 3: 0.0394 usec per loop
~$ python -m timeit —setup "l=range(1000)" "l[True]"
10000000 loops, best of 3: 0.0393 usec per loop
~$ 
~$
~$ python -m timeit —setup "l=range(1000)" "l[0]"
10000000 loops, best of 3: 0.0232 usec per loop
~$ python -m timeit —setup "l=range(1000)" "l[0]"
10000000 loops, best of 3: 0.0232 usec per loop
~$ python -m timeit —setup "l=range(1000)" "l[0]"
10000000 loops, best of 3: 0.0232 usec per loop
~$ python -m timeit —setup "l=range(1000)" "l[1]"
10000000 loops, best of 3: 0.0232 usec per loop
~$ python -m timeit —setup "l=range(1000)" "l[1]"
10000000 loops, best of 3: 0.0232 usec per loop
~$ python -m timeit —setup "l=range(1000)" "l[1]"
10000000 loops, best of 3: 0.0232 usec per loop

但我不知道根本原因是什么。我的意思是为什么评估TrueFalse 需要更多时间?在基准测试期间,我还注意到了另一件神秘的事情。在基准测试的第一部分,结果有所不同,而第二部分的数字是稳定的。

【问题讨论】:

  • booleaninteger等价was already asked here的区别。
  • 好的,谢谢。但是该线程没有讨论关于真/假评估的数字的不稳定性。
  • 不稳定很容易:缓存未命中。由于您需要进行查找,因此无法保证您在缓存中命中它,或者您需要去 ram 获取它,或者在基准运行时仍保留在缓存中。所以不稳定。

标签: python performance boolean internals


【解决方案1】:

对于l[k==1]l[0 if k==1 else 1],您的时间不够长。您看到的差异在您从随机变化中获得的范围内。我不确定哪种形式最终更快,但更长的试验显示了相反的效果:

>>> timeit.timeit('l[k==1]', 'l=[1,2];k=1', number=100000000)
10.782931089401245
>>> timeit.timeit('l[0 if k==1 else 1]', 'l=[1,2];k=1', number=100000000)
11.140317916870117

l[0 if k==1 else 1] 出人意料地具有竞争力很可能是因为l[k==1] 没有命中fast pathBINARY_SUBSCR 操作码:

TARGET_NOARG(BINARY_SUBSCR)
{
    w = POP();
    v = TOP();
    if (PyList_CheckExact(v) && PyInt_CheckExact(w)) {
        /* INLINE: list[int] */
        Py_ssize_t i = PyInt_AsSsize_t(w);
        if (i < 0)
            i += PyList_GET_SIZE(v);
        if (i >= 0 && i < PyList_GET_SIZE(v)) {
            x = PyList_GET_ITEM(v, i);
            Py_INCREF(x);
        }
        else
            goto slow_get;
    }
    else
      slow_get:
        x = PyObject_GetItem(v, w);

在您的第二个测试中,还有一个额外的因素是,在 Python 2 中,True 是一个内置变量查找,而1 是一个更快的LOAD_CONSTLOAD_CONST 仅索引到代码对象的 co_consts 元组,而内置查找需要两次 dict 查找。

【讨论】:

    【解决方案2】:

    booleaninteger除法have been asked earlier的区别。但是,没有讨论它的(不)稳定性。下面是我的分数:

    Python2

    ~$ python2 -m timeit --setup "l=range(1000)" "l[False]"
    10000000 loops, best of 3: 0.0366 usec per loop
    ~$ python2 -m timeit --setup "l=range(1000)" "l[False]"
    10000000 loops, best of 3: 0.0332 usec per loop
    ~$ python2 -m timeit --setup "l=range(1000)" "l[1]"
    10000000 loops, best of 3: 0.0193 usec per loop
    ~$ python2 -m timeit --setup "l=range(1000)" "l[1]"
    100000000 loops, best of 3: 0.0194 usec per loop
    ~$ python2 -m timeit --setup "l=range(1000)" "l[1]"
    100000000 loops, best of 3: 0.0195 usec per loop
    ~$ python2 -m timeit --setup "l=range(1000)" "l[0]"
    100000000 loops, best of 3: 0.0196 usec per loop
    

    Python 3

    ~$ python -m timeit --setup "l=range(1000)" "l[0]"
    10000000 loops, best of 3: 0.0712 usec per loop
    ~$ python -m timeit --setup "l=range(1000)" "l[0]"
    10000000 loops, best of 3: 0.072 usec per loop
    ~$ python -m timeit --setup "l=range(1000)" "l[0]"
    10000000 loops, best of 3: 0.0719 usec per loop
    ~$ python -m timeit --setup "l=range(1000)" "l[False]"
    10000000 loops, best of 3: 0.082 usec per loop
    ~$ python -m timeit --setup "l=range(1000)" "l[False]"
    10000000 loops, best of 3: 0.0821 usec per loop
    

    有趣的是:我的分数不仅在 Python 版本之间发生变化,而且在 Python 版本中发生变化。由于cache misses,差异是合乎逻辑的。有趣的是,01 的差异是如此之小,您无法看到 4 位小数...(我使用的是虚拟机,所以这可能会降低我的系统速度,足以使很容易看出区别)

    【讨论】:

      猜你喜欢
      • 2011-07-31
      • 2010-12-22
      • 2010-10-05
      • 2012-09-23
      • 1970-01-01
      • 1970-01-01
      • 2013-06-29
      • 2019-02-26
      相关资源
      最近更新 更多