Python 列表查找比元组快?

Python 列表查找比元组快?,python,performance,list,tuples,python-internals,Python,Performance,List,Tuples,Python Internals,过去,当我在一个紧密循环中需要类似数组的索引查找时,我通常使用元组,因为它们通常性能非常好(接近于只使用n个变量)。然而,我今天决定质疑这一假设,并得出了一些令人惊讶的结果: In [102]: l = range(1000) In [103]: t = tuple(range(1000)) In [107]: timeit(lambda : l[500], number = 10000000) Out[107]: 2.465047836303711 In [108]: timeit(lambd

过去,当我在一个紧密循环中需要类似数组的索引查找时,我通常使用元组,因为它们通常性能非常好(接近于只使用n个变量)。然而,我今天决定质疑这一假设,并得出了一些令人惊讶的结果:

In [102]: l = range(1000)
In [103]: t = tuple(range(1000))
In [107]: timeit(lambda : l[500], number = 10000000)
Out[107]: 2.465047836303711
In [108]: timeit(lambda : t[500], number = 10000000)
Out[108]: 2.8896381855010986
元组查找比列表查找花费的时间长17%!反复实验得出了类似的结果。分解每一个,我发现它们都是:

In [101]: dis.dis(lambda : l[5])
  1           0 LOAD_GLOBAL              0 (l)
              3 LOAD_CONST               1 (5)
              6 BINARY_SUBSCR       
              7 RETURN_VALUE    
作为参考,典型的10000000全局变量查找/返回需要2.2秒。另外,我在没有lambdas的情况下运行了它,以防万一(注意数字=100000000而不是10000000)

在这里,元组查找需要花费35%的时间。这是怎么回事?对于非常紧密的循环,这实际上似乎是一个显著的差异。这可能是什么原因造成的

请注意,对于分解为变量(例如,x,y=t)而言,元组的速度稍快一些(在我的几个测试中,时间缩短了约6%),而对于从固定数量的参数进行构造而言,元组的速度快得惊人(时间缩短了约83%)。不要把这些结果当作一般规则;我只是执行了一些小型测试,这些测试对于大多数项目来说都是毫无意义的

In [169]: print(sys.version)
2.7.1 (r271:86882M, Nov 30 2010, 09:39:13) 
[GCC 4.0.1 (Apple Inc. build 5494)]

与此相反,我有完全不同的建议

如果根据问题的性质,数据的长度是固定的,那么使用元组

示例:

  • (r,g,b)-三个元素,由问题的定义确定
  • (纬度、经度)-两个元素,由问题定义修复
如果根据问题的性质,数据是变量,请使用列表

速度不是问题所在


意义应该是唯一的考虑因素。

元组主要用于构造列表,而不是访问列表

元组的访问速度应该稍快一些:它们需要较少的间接寻址。然而,我相信主要的好处是,在构建列表时,它们不需要第二次分配

查找速度稍快的原因列表是因为Python引擎对其进行了特殊优化:

case 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);
        }
case BINARY\u SUBSCR:
w=POP();
v=顶部();
if(PyList_CheckExact(v)和PyInt_CheckExact(w)){
/*内联:列表[int]*/
Py_ssize_t i=PyInt_AsSsize_t(w);
if(i<0)
i+=PyList\u GET\u尺寸(v);
如果(i>=0&&i
有了这个优化,元组比列表稍微快一点(大约4%)


请注意,在这里为元组添加单独的特例优化不是一个好主意。VM循环主体中的每一个这样的特例都会增加代码大小,从而降低缓存一致性,这意味着每一种其他类型的查找都需要一个额外的分支。

我有点惊讶——尽管我没有得到35%的差异如果您声称接近13%,您是使用函数调用还是使用字符串运行timeit?如果我将它们包装到函数中,我会接近13%(17%)(参见第一个结果)。我认为返回值和可能的加载值使这些值更接近。mac os x leopar,python 3.0,运行相同的代码:列表为10.0xx,tuple.strings为3.5xx。python 2.7.1,Windows XP,古代AMD Sempron。我还要补充一点,性能上的17%差异很少能阻止显示。通常,需要当它增加到117秒时,100秒的运行时间并不是不可接受的。对于使用python构建的大多数应用程序,我同意你的观点,但人们确实构建了具有紧密循环的应用程序,它们需要性能,但不想花时间跳到C(或Cython,等等)要明白这一点。在某些地方使用列表而不是元组是一种非常简单的方法,至少也是一种可读性很强的方法,可以挤出一点。此外,正如我所说的“从固定数量的参数构造元组的速度要快得多”。我在这里处理的大多是可变长度序列。我一直在做“元组(我的列表)”之类的事情在我的大循环之前。@Steven:这是lambda中的17%。除去函数调用和返回的开销,它是35%,这可能是很重要的。但是,是的,你是对的;对于大多数应用程序来说,这是一个不成问题的问题。大多数情况下,我只是觉得它真的很奇怪。@Bryan你应该在编码时停止进行微优化。编写最干净的代码史蒂文:我主要是对语言实现之类的东西感到好奇。然而,在我提出这个问题的例子中,我正在构建一个大型的不可变列表,最初是由大量紧密循环中使用的理解构建的。在这种情况下,任何一种数据结构都是合适的(元组用于强制执行不变性,列表用于构造中更可读的列表理解)。此更改很简单,保留并可以提高可读性,并为我的应用程序生成更快的代码。感谢Glenn!这正是我想要的!奇怪的是,这是ceval.c的第1374行。py3k中没有这样的优化
case 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);
        }