🌠 Python速度アップ考

2021/10/23

🌠 Python速度アップ考  最近はプログラムをいじることはありませんが、マーク・ルッツ著「Learning Python」をパラパラと読み返しています。 Python でコーディングした今はなるほどという部分も多いです。 この本の中でも、Pypy の速さに触れていますが、Python の弱点といわれるループ計算とその代替手段について調べているとことがあります。 面白いので、自分でも調べてみようと思い、本では触れられていない、Pypy での結果に加え、numpy, Cython, cupy でも計測してみました。 計測に使ったソースは以下になります (本のプログラムに手を加えました。 ちなみに書物のプログラムは公開可と書いてあります)。


import sys, time
import numpy as np # 追加
reps = 10000000
repslist = list(range(reps))
repslistn = np.arange(0, reps) # 追加

timer = time.clock if sys.platform[:3] == 'win' else time.time

def forLoop():
    res = []
    for x in repslist:
        res.append(abs(x))
    return res

def listComp():
    return [abs(x) for x in repslist]

def mapCall():
    return list(map(abs, repslist))

def genExpr():
    return list(abs(x) for x in repslist)

def genFunc():
    def gen():
        for x in repslist:
            yield abs(x)
    return list(gen())

def numPy(): # 追加
    return np.abs(repslistn).copy()

for func in (forLoop, listComp, mapCall, genExpr, genFunc, numPy): # 追加
    start = time.time()
    res = func()
    print('%9s %1.5f' % (func.__name__, time.time() - start))

 そして、そのその結果が次表です。 Cythonの結果はここに書いていませんが、forループで0.30530秒、内包表記で0.37105秒でした。 ただ、これは変数の型指定などが適当で、修正の余地もあり、表には書き入れませんでした (forループが速いのはC言語がそれを不得意にしていないからでしょう)。

計算方法 Python3 Pypy3 Pypy2
forループ 0.75638 0.43605 0.42708
内包表記 0.54036 0.06576 0.05122
map 0.21357 0.31909 0.09990
式ジェネレータ 0.55462 0.41918 0.38205
関数〃 0.55665 0.40569 0.35905
numPy 0.15842 0.06621 -
cuPy 0.00729 - -
単位: 秒

 すぐにわかるのは、いずれの Python にしろ、for ループは遅く、numpy やリスト内包表記が速いということです。 式ジェネレータ、関数ジェネレータというのは、yield を使い、関数の戻り値を漸次返し、時間消費の効率化を図る比較的新しい手法です。

 この中では、Pypyでは内包表記を使う方法、Python3ではnumpyを使う方法がスピードアップにとって比較的いい感じがします。 GPU を使う cupy はforループを使うより100倍以上速いですが、これは別格で、CPU 処理とのやりとりが多いとかなりスピードダウンするとも聞きますし、ディープラーニングや機械学習の必要が生じたときに使うもののような気がします。

 スピードの追求はきりがないですが、まあ速いにこしたことはないわけで、いろいろ考えさせられます。

twitterシェア Facebookシェア
  コメントの注意点