パフォーマンス測定とNumPyを用いた簡単な高速化|Pythonにおける処理高速化をラフに考える #1

f:id:lib-arts:20201113203327j:plain

基本的にプログラミングは動けば良い派で可読性の高さを優先することが多いですが、処理の高速化が必要な際もあるので、Pythonを用いた処理高速化について考える新規シリーズを作成いたしました。
最初から難しく考えると大変なので、しばらくは緩めに進め、徐々に内容を本格化していければと思います。(しばらくは体系立った解説にはしませんので、そういう意味ではあまり参考にしないようにお願いします)
#1では繰り返し文の処理をmapを用いて置き換えた際の簡単なパフォーマンス比較を行います。
以下、目次になります。
1. パフォーマンス測定に関して
2. mapを用いた繰り返し文の置き換えについて
3. まとめ

 

1. パフォーマンス測定に関して
1節ではパフォーマンス測定に関して取り扱います。Jupyterでは「%%time」を用いるとある程度簡単に処理にかかった時間の計測ができるので、当シリーズでは一旦「%%time」を用いるものとし、以下簡単にご紹介します。

%%time

a = 1
print(a)

まずは上記を実行してみます。実行結果は下記のようになります。

f:id:lib-arts:20201113203529p:plain
上記ではtotalが87µsとなっており、ほぼ一瞬で終了していることがわかります。あまり軽い処理だとパフォーマンス測定には適していないので、以下、繰り返し文を用いて少し処理の負荷を大きくしてみます。

%%time

a = 0
for i in range(10000):
    idx = i+1
    a += idx

print(a)

f:id:lib-arts:20201113203622p:plain

上記ではtotalが1.86msとなっており、最初のプログラムより若干時間がかかっていることがわかります。以下、ループの数を増やしてもう少し実験してみます。

f:id:lib-arts:20201113203932p:plain

上記は、ループ回数をそれぞれ10万、500万、1億にして実験してみた結果です。徐々に時間がかかるようになり、ループが1億だと10秒以上かかる結果となりました。このように「%%time」を用いることで、Jupyter上で簡単に計測を行うことができるので、しばらくはこちらを用いてパフォーマンス測定を行なっていくものとします。

パフォーマンス測定について簡単な確認はできましたので1節はここまでとします。

 

2. NumPyを用いた簡単な高速化
2節では1節のプログラムの高速化について考えます。Pythonにおいては繰り返し文(for文など)は遅いと言われることが多いので、NumPyを用いて1節で取り扱ったプログラムの高速化を試みてみます。

%%time

x = np.arange(100000000)+1
print(np.sum(x))

上記を実行すると下記のようになります。

f:id:lib-arts:20201113204935p:plain

計算結果自体は1節の1〜1億までの和を計算した結果と同じですが、処理時間についてはtotalで849msと、1節の15分の1程度に抑えられていることがわかります。このようにちょっとした処理を書き換えるだけでも、高速化が可能になります。もう少し実験を行なってみます。

f:id:lib-arts:20201113210717p:plain
上記はさらにもう1桁上げ、1〜10億までの結果を計算して比較した結果です。繰り返し文を用いた実装の方が時間はかかっていますが、NumPyの方もそれなりにかかっていることがわかります。

f:id:lib-arts:20201113210042p:plain

確認するとNumPyの実装の方がsysにかかる時間が多いので、totalの比とuserの比に分けて計算してみた結果が上記です。NumPyはuserよりもsysに時間がかかりやすいことがある程度見て取れます。

このように単純な和を計算するプログラムでも様々な要素が絡んでくるので、色々と試しつつ内部処理なども含めて高速化を行なっていくのが良さそうです。今回はラフに確認を行えれば十分だったのでここまでとします。


3. まとめ
#1では「%%time」を用いたパフォーマンス測定と、NumPyを用いた簡単な処理の高速化について取り扱いました。
#2ではmapを用いた繰り返し文(for文)の書き換えについて確認します。