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