読者です 読者をやめる 読者になる 読者になる

mapと内包表記の速度の差について

Python

リストに対して何らかの処理をする場合色々な方法があります。それらの速度について勘違いしていたことがあり、@mopemope氏からもご指摘をいただいたので、まずは何も言わずに下記のコードを実行してみます。

import timeit

N = 200
target = range(N)

def func(x):
    return x * x

def f1():
    ret = []
    for x in target:
        ret.append(func(x))
    return ret

def f2():
    ret = [0] * len(target)
    for i in xrange(len(ret)):
        ret[i] = func(i)
    return ret

def f3():
    return [func(x) for x in target]

def f4():
    return map(func, target)

assert f1() == f2() == f3() == f4()

setup = 'from __main__ import %s' % ', '.join(locals())

print timeit.Timer('f1()', setup=setup).repeat(3, 10000)
print timeit.Timer('f2()', setup=setup).repeat(3, 10000)
print timeit.Timer('f3()', setup=setup).repeat(3, 10000)
print timeit.Timer('f4()', setup=setup).repeat(3, 10000)

実行結果はこうなります。

[0.9627680778503418, 0.98267292976379395, 0.96235084533691406]
[0.83197402954101562, 0.83204221725463867, 0.83305907249450684]
[0.70136713981628418, 0.70795083045959473, 0.69733786582946777]
[0.67866301536560059, 0.67679786682128906, 0.67791295051574707]

@mopemope氏がおっしゃる通り、mapが速いわけです。理由は実行されるバイトコードが少ないのと、配列を冒頭でガッツリとメモリ上に確保するから、とのことでしたが、まだ自分自身の目で確認できていないので詳細は割愛いたします。


ところで今まで私は「mapは遅いから内包表記を使おう」とずっと考えていたのですが、その考えに至った実験コードは下記のようなものでした。

import timeit

N = 200

def f3():
    return [x * x for x in xrange(N)]

def f4():
    return map(lambda x: x * x, xrange(N))

assert f3() == f4()

setup = 'from __main__ import %s' % ', '.join(locals())

print timeit.Timer('f3()', setup=setup).repeat(3, 10000)
print timeit.Timer('f4()', setup=setup).repeat(3, 10000)

実行結果はこちらになります。

[0.38262510299682617, 0.37773680686950684, 0.37621688842773438]
[0.71781802177429199, 0.71918988227844238, 0.71978497505187988]

とまぁ、これだけ見て「map遅すぎる」と結論づけてしまったわけですね。これはmapと内包表記の差ではなく、おそらく関数呼び出しのコストの差が大きいのでしょう。


というわけで今後は内包表記とmapを適度に使い分けようかな、と思っています。関数が存在する場合はmapを使用して、関数が無い場合は内包表記を使用する、という感じです。


何はともあれ、こういうことをサラリと話せるようになっているとカッコイイですね。精進しなければなりません。


リバースエンジニアリング ―Pythonによるバイナリ解析技法 (Art Of Reversing)

リバースエンジニアリング ―Pythonによるバイナリ解析技法 (Art Of Reversing)