LoginSignup
8
2

More than 5 years have passed since last update.

本当に内包表記はどの場合においても早いのか?検証してみたら面白かった。

Last updated at Posted at 2018-05-27

はじめに

Pythonで作るエラトステネスのふるい」という記事を遊びで書いたのですが、コメントで指摘をして頂いて改善をすることができました。そのコメントの中で

ちなみに data = list(range(2, n + 1)) とも書けますが、遅くなるようです。

と書かれていました。私もlistオブジェクトへのキャストの方が、内包表記で展開するよりも遅いだろうと思ったのですが、ちゃんと検証したことはなかったのでやってみるかぁっと思ってやってみたよって話です。

逆アセンブル

disモジュールを使うことで、CPythonバイトコードを逆アセンブルできるのでやってみました。

>>> from dis import dis
>>> def hoge():
...     return [i for i in range(10)]
... 
>>> dis(hoge)
  2           0 LOAD_CONST               1 (<code object <listcomp> at 0x---------, file "<stdin>", line 2>)
              2 LOAD_CONST               2 ('hoge.<locals>.<listcomp>')
              4 MAKE_FUNCTION            0
              6 LOAD_GLOBAL              0 (range)
              8 LOAD_CONST               3 (10)
             10 CALL_FUNCTION            1
             12 GET_ITER
             14 CALL_FUNCTION            1
             16 RETURN_VALUE
>>> def foo():
...     return list(range(10))
... 
>>> dis(foo)
  2           0 LOAD_GLOBAL              0 (list)
              2 LOAD_GLOBAL              1 (range)
              4 LOAD_CONST               1 (10)
              6 CALL_FUNCTION            1
              8 CALL_FUNCTION            1
             10 RETURN_VALUE

※「0x---------」はメモリアドレスのため隠しています。

この結果、ステップ数はlist(range(10))の方が少ないことが分かりました。つまり、内方表記の方が早いという理解は間違っていたのかもしれないです。

実際に測定してみる

そこで、次のようなソースコードで検証を行いました。

# -* -coding:utf-8 -*-
import time
from dis import dis
import numpy as np
from matplotlib import pyplot as plt


def when_list(n):
    return list(range(2, n + 1))


def when_inner(n):
    return [i for i in range(2, n + 1)]


def measurement():
    list_time = []
    inner_time = []
    count = []
    for i in range(1, 100):
        n = (i + 1) * 10
        s_time = time.time()
        when_list(n)
        list_time.append(time.time() - s_time)
        s_time = time.time()
        when_inner(n)
        inner_time.append(time.time() - s_time)
        count.append(n)
    plt.subplot(111)
    plt.plot(np.array(count), np.array(list_time), label="list")
    plt.plot(np.array(count), np.array(inner_time), label="inner")
    leg = plt.legend(loc='best', ncol=2, mode="expand", shadow=True, fancybox=True)
    leg.get_frame().set_alpha(0.5)
    plt.show()


def main():
    measurement()


if __name__ == '__main__':
    main()

実証結果

こちらの結果は次のようになりました。(10回とりました。)

result1.png
result2.png
result3.png
result4.png
result5.png
result6.png
result7.png
result8.png
result9.png
result10.png

結論

実証結果から、最初の理解は誤りであることがことが分かりました。
ということで、本当に内包表記が常に早いわけではないことが分かりました。
ただし、time関数の測定精度が悪いので数値として内包表記がこれだけ遅いと言うのは難しいですが。

8
2
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
2