timeit で計測する。
time よりも詳細な時間を計測することができます。
timeit は、コマンドラインで次のように打つことで、 実行時間を計測してくれます。
python -m timeit "Pythonのコード"
timeit の使い方、出力される結果の意味などについて、 以下の記事でご紹介させていただきました。 このさき、ずっと使うツールになるので、ざっくりとで構わないので、 もしご存知でなければ簡単に目を通しておいてください。
# リテラルの参照
python -m timeit "1;1;1"
# 変数の参照
python -m timeit -s "a=1" "a;a;a"
$ # リテラルの参照
$ python -m timeit "1;1;1"
100000000 loops, best of 3: 0.00956 usec per loop
$
$ # 変数の参照
$ python -m timeit -s "a=1" "a;a;a"
10000000 loops, best of 3: 0.0435 usec per loop
$
# リテラルの参照
python -m timeit "1" "1" "1"
# 変数の参照
python -m timeit -s "a=1" "a" "a" "a"
$ # リテラルの参照
$ python -m timeit "1" "1" "1"
100000000 loops, best of 3: 0.00951 usec per loop
$
$ # 変数の参照
$ python -m timeit -s "a=1" "a" "a" "a"
10000000 loops, best of 3: 0.0454 usec per loop
$
# リテラルの参照
python -m timeit "'Hello';'Hello';'Hello'"
# 変数の参照
python -m timeit -s "s='Hello'" "s;s;s"
$ # リテラルの参照
$ python -m timeit "'Hello';'Hello';'Hello'"
100000000 loops, best of 3: 0.00995 usec per loop
$
$ # 変数の参照
$ python -m timeit -s "s='Hello'" "s;s;s"
10000000 loops, best of 3: 0.0472 usec per loop
$
# リテラルの参照
python -m timeit "'Hello'" "'Hello'" "'Hello'"
# 変数の参照
python -m timeit -s "s='Hello'" "s" "s" "s"
$ # リテラルの参照
$ python -m timeit "'Hello'" "'Hello'" "'Hello'"
100000000 loops, best of 3: 0.0103 usec per loop
$
$ # 変数の参照
$ python -m timeit -s "s='Hello'" "s" "s" "s"
10000000 loops, best of 3: 0.0436 usec per loop
$
# リテラルの参照
python -m timeit "(1,2,3);(1,2,3);(1,2,3)"
# 変数の参照
python -m timeit -s "a=(1,2,3)" "a;a;a"
$ # リテラルの参照
$ python -m timeit "(1,2,3);(1,2,3);(1,2,3)"
10000000 loops, best of 3: 0.0577 usec per loop
$
$ # 変数の参照
$ python -m timeit -s "a=(1,2,3)" "a;a;a"
10000000 loops, best of 3: 0.0428 usec per loop
$
# リテラルの参照
python -m timeit "(1,2,3)" "(1,2,3)" "(1,2,3)"
python -m timeit -s "a=(1,2,3)" "a" "a" "a"
$ # リテラルの参照
$ python -m timeit "(1,2,3)" "(1,2,3)" "(1,2,3)"
10000000 loops, best of 3: 0.0219 usec per loop
$ python -m timeit -s "a=(1,2,3)" "a" "a" "a"
10000000 loops, best of 3: 0.0237 usec per loop
$
list だけ変数を参照した方が速い。
# リテラルの参照
python -m timeit "[1,2,3];[1,2,3];[1,2,3]"
# 変数の参照
python -m timeit -s "a=[1,2,3]" "a;a;a"
$ # リテラルの参照
$ python -m timeit "[1,2,3];[1,2,3];[1,2,3]"
10000000 loops, best of 3: 0.186 usec per loop
$ # 変数の参照
$ python -m timeit -s "a=[1,2,3]" "a;a;a"
10000000 loops, best of 3: 0.0222 usec per loop
# リテラルの参照
python -m timeit "[1,2,3]" "[1,2,3]" "[1,2,3]"
# 変数の参照
python -m timeit -s "a=[1,2,3]" "a" "a" "a"
$ # リテラルの参照
$ python -m timeit "[1,2,3]" "[1,2,3]" "[1,2,3]"
10000000 loops, best of 3: 0.185 usec per loop
$ python -m timeit -s "a=[1,2,3]" "a" "a" "a"
10000000 loops, best of 3: 0.0228 usec per loop
答え: リストの場合、リテラルを参照するとオブジェクトが生成されます。
LOAD_CONST
は定数の読み込みです。
tuple の場合、LOAD_CONST
だけで済んでいます。
一方で list の場合、BUILD_LIST
という
list を生成する処理が走ってしまっています。
import dis
dis.dis("[1, 2, 3]")
dis.dis("(1, 2, 3)")
>>> import dis
>>> dis.dis("[1, 2, 3]")
1 0 LOAD_CONST 0 (1)
2 LOAD_CONST 1 (2)
4 LOAD_CONST 2 (3)
6 BUILD_LIST 3
8 RETURN_VALUE
>>> dis.dis("(1, 2, 3)")
1 0 LOAD_CONST 0 ((1, 2, 3))
2 RETURN_VALUE
>>>
しれっと結論だけ述べて dis について何も説明していませんでした。 Python はコードを実行するときに、バイトコードという 中間言語に変換されて、それからコードが実行されています。 dis は、その中間コードを表示してくれる標準ライブラリです。
自分も全然理解していないので、 そんなものもあるんだなくらいに押さえておいていただければと思います。
リテラルの参照は、オブジェクトの生成→参照という2段階を踏んで、 単純に変数の参照するよりも重くなるかなと思っていました。 しかし実際には int, str, tuple については、 変数よりもリテラルを参照した方が早いことがわかりました。