21

以前は、タイトなループで配列のようなインデックスルックアップが必要な場合、タプルは一般的に非常にパフォーマンスが高いように見えるため(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    

参考までに、一般的な10,000,000個のグローバル変数のルックアップ/リターンには2.2秒かかります。また、念のため、ラムダなしで実行しました(10,000,000ではなくnumber = 100,000,000であることに注意してください)。

In [126]: timeit('t[500]', 't=range(1000)', number=100000000)
Out[126]: 6.972800970077515
In [127]: timeit('t[500]', 't=tuple(range(1000))', number=100000000)
Out[127]: 9.411366939544678

ここでは、タプルルックアップに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)]
4

2 に答える 2

23

タプルは、リストにアクセスするためではなく、リストを作成するために主に高速です。

タプルへのアクセスはわずかに高速である必要があります。必要な間接参照が1つ少なくなります。ただし、主な利点は、リストを作成するときに2番目の割り当てを必要としないことです。

リストのルックアップがわずかに高速である理由は、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);
        }

この最適化がコメント化されているため、タプルはリストよりもわずかに高速です(約4%)。

ここでタプルに個別の特殊なケースの最適化を追加することは、必ずしも良い考えではないことに注意してください。VMループの本体でこのような特殊なケースが発生すると、コードサイズが大きくなり、キャッシュの一貫性が低下します。つまり、他のすべてのタイプのルックアップには追加のブランチが必要になります。

于 2011-04-11T19:56:34.073 に答える
12

これとは反対に、私にはまったく異なるアドバイスがあります。

データが(問題の性質上)長さが固定されている場合は、タプルを使用します。

例:

  • (r、g、b)-問題の定義によって修正された3つの要素。
  • (緯度、経度)-2つの要素、問題の定義によって修正されました

データが-問題の性質上-変数である場合は、リストを使用します。

速度は問題ではありません

意味が唯一の考慮事項である必要があります。

于 2011-04-11T19:08:19.153 に答える