high performance python computing for data science
TRANSCRIPT
High Performance Python Computing for Data Science
~データ分析でPythonを高速化したいときに見る何か~
株式会社ブレインパッド
佐藤 貴海
@tkm2261
PyData.Tokyo Meetup #4
2015年4月3日 PyData.Tokyo Meetup #4 1
所属
自己紹介
2015年4月3日 PyData.Tokyo Meetup #4 3
Twitter ID tkm2261
専門 経営工学/最適化
株式会社ブレインパッド
業務 分析何でも屋さん(研究開発)
機械学習との出会い
当時の研究が実用性
皆無
精神の逃げ道として
機械学習 を趣味で開始
研究が
詰んで、 趣味が本職に
言語、画像と幅広く
遊んでます
趣味で始めたので
バックグラウンド
2015年4月3日 PyData.Tokyo Meetup #4 9
マニアック過ぎて、人に話したことありませんでしたが・・・
半正定値計画問題(SDP)を行列の疎性を使って高速に解くことを研究
引用:私の卒論
C++で数値計算したり、疎行列演算を自前実装してました。 なので、今日はちょっと語らせてください・・・
効率より移植性
『UNIXという考え方』という書籍でも
2015年4月3日 PyData.Tokyo Meetup #4 12
「プログラムを速くすることに時間をかけない」
「C言語プログラムを書くのは、絶対に必要な場合以外避けるべきだ」
「多くのUNIXプログラマが犯す間違いの一つに、僅かな速度を求めてシェルスクリプトをC言語で書き直すというものがある。それは、時間の無駄だ」
移植性を損なう、
過度な高速化は基本NG
移植性はPythonだから若干あるものの・・・
今回紹介するNumbaやCythonはメソッドを関数で書き換えたりして、
可読性を結構損ないます
2015年4月3日 PyData.Tokyo Meetup #4 13
Python高速化の選択肢
2015年4月3日 PyData.Tokyo Meetup #4 16
ハードウェア レベル
ソフトウェア レベル
コンピュータレベル
言語レベル
コーディングレベル
独断と偏見による、データサイエンティストが利用可能なレベルの高速化選択肢
Hadoop Streaming
Spark
H2o (機械学習限定)
マルチプロセス計算 (multiprocessing)
GPGPU (Numba)
C拡張(Cython)
JITコンパイル(PyPy, Numba)
BLAS最適化
クラスタレベル
リスト内包表記 辞書型の活用
Numpy等の利用 疎行列の利用
(番外編)プロファイラの利用
クラスタレベルの高速化
2015年4月3日 PyData.Tokyo Meetup #4 17
ハードウェア レベル
ソフトウェア レベル
言語レベル
コーディングレベル
C拡張(Cython)
JITコンパイル(PyPy, Numba)
BLAS最適化
リスト内包表記 辞書型の活用
Numpy等の利用 疎行列の利用
(番外編)プロファイラの利用
コンピュータレベル
マルチプロセス計算 (multiprocessing)
GPGPU (Numba)
Hadoop Streaming
Spark
H2o (機械学習限定)
クラスタレベル
クラスタレベルの高速化
2015年4月3日 PyData.Tokyo Meetup #4 19
Hadoop Streaming
標準入出力経由で任意言語でMapReduceを使えるライブラリ
数時間以上かかるプログラムで、お金があるときの選択肢
メリット
デメリット
Amazon EMRで簡単構築
MapReduceなので多段処理に弱い
最近話題のオープンソース分散機械学習ライブラリ
メリット 機械学習アルゴリズムの網羅率が高い(RBF, GBT, GLM, DNN)
デメリット ETL(データ処理)はまた別途必要
クラスタレベルの高速化
2015年4月3日 PyData.Tokyo Meetup #4 20
数時間以上かかるプログラムで、お金があるときの選択肢
ポストMapReduceの最有力 分散処理フレームワーク
https://spark.apache.org/
クラスタレベルの高速化
2015年4月3日 PyData.Tokyo Meetup #4 22
Sparkに
DataFrameがあって
ML Pipelinesがあって
MLlib※がある
分散環境でETL全般が出来て、
しっかりとしたデータ構造があり
処理が抽象化された
機械学習が出来る
現実 超解釈
※ML Pipelinesに統合されるかも
クラスタレベルの高速化
2015年4月3日 PyData.Tokyo Meetup #4 23
データサイエンティストの分析基盤はSparkになっていきそう
( Rの人は早い内にPythonかScala覚えた方が良いかも)
BDAS (the Berkeley Data Analytics Stack)
https://amplab.cs.berkeley.edu/software/
+ Velox
コンピュータレベルの高速化
2015年4月3日 PyData.Tokyo Meetup #4 24
ハードウェア レベル
ソフトウェア レベル
言語レベル
コーディングレベル
Hadoop Streaming
Spark
H2o (機械学習限定)
C拡張(Cython)
JITコンパイル(PyPy, Numba)
BLAS最適化
クラスタレベル
リスト内包表記 辞書型の活用
Numpy等の利用 疎行列の利用
(番外編)プロファイラの利用
コンピュータレベル
マルチプロセス計算 (multiprocessing)
GPGPU (Numba)
マルチプロセス計算 (multiprocessing)
Pythonが複数スレッドで実行されないためのロック
2015年4月3日 PyData.Tokyo Meetup #4 25
GIL (Global Interpreter Lock)
こいつのせいでデフォルトではシングルスレッドでしか動かない
複数プロセスを立ち上げる (multiprocessing)
GILのない言語を使う (マルチスレッド)
対策としては2つ、
今回は対象外
マルチプロセスとマルチスレッドの違い
2015年4月3日 PyData.Tokyo Meetup #4 26
マルチプロセス プロセス間はメモリ非共有
マルチスレッド 同一プロセスのスレッド間はメモリ共有
とりあえずの理解としては以下
プロセスが親で、スレッドが子という認識でOK
マルチプロセスの紹介
2015年4月3日 PyData.Tokyo Meetup #4 27
複数テキストファイルを形態素解析している時のtop画面
ひとつのプロセスはPythonなのでCPU使用率が100%を超えない
処理が完全に独立した用途なので、マルチプロセスで問題なし
マルチスレッドの紹介
2015年4月3日 PyData.Tokyo Meetup #4 28
scikit-learnのランダムフォレスト実行時のtop画面
ひとつのプロセスで%CPUが100%超なのでマルチスレッド
機械学習系は各処理が関連するのでマルチスレッドが効果的
昔はマルチプロセスだったが・・・
2015年4月3日 PyData.Tokyo Meetup #4 29
マルチスレッド化で相当恩恵があった模様
scikit-learnもver0.15より前はマルチプロセスだったが
multiprocessingの実装
Pythonのみで出来るマルチプロセスを今日は紹介
ここに色々書いてあるが、データサイエンティストには重いので
2015年4月3日 PyData.Tokyo Meetup #4 30
http://docs.python.jp/2.7/library/multiprocessing.html
Pool.map()
Pool.apply_sync()
Process()
この3つを紹介
Pool.map()
もっとも手軽に並列計算できるのがPool.map()
2015年4月3日 PyData.Tokyo Meetup #4 31
from multiprocessing import Pool import math pool = Pool(processes=4) # 引数なしなら、最大コア数 print pool.map(math.sqrt, range(10)) # 0~10要素の平方根が表示 pool.close() pool.join()
メリット • 超手軽 • 普通のmap関数と入れ替えてデバッグが容易
デメリット
• 並列化前にはmap関数での実装は稀なので、要リファクタ
• 並列化対象関数の引数が1個限定なので、要リファクタ
• 関数と引数がシリアライズ(pickle)出来ないと使えない ※後述
後処理が重い場合はPool.imap()でイテレータ取得の方が良い ただしchunksizeの設計が面倒
Pool.apply_async()
2015年4月3日 PyData.Tokyo Meetup #4 32
for文を簡単に並列化したいときにオススメ from multiprocessing import Pool import math pool = Pool(processes=4) # 引数なしなら、最大コア数 list_processes = [] for i in range(10): # math.pow(x, y)はxのy乗を返す関数 list_processes.append(pool.apply_async(math.pow, (i, 0.5))) # 引数を複数設定可能 list_results = [process.get() for process in list_processes] print list_results pool.close() pool.join()
メリット • 並列化前のfor文をそのまま残せる
デメリット • map関数よりは記述量が多い
• 関数と引数がシリアライズ(pickle)出来ないと使えない ※後述
Process()
2015年4月3日 PyData.Tokyo Meetup #4 33
関数と引数がシリアライズ出来ない時にはコレ、結果は共有メモリで取得
from multiprocessing import Process, Manager def sqrt(x, map_results): map_results[x] = x**0.5 if __name__ == '__main__': list_processes = [] manager = Manager() map_results = manager.dict() for i in range(10): process = Process(target=sqrt, args=(i, map_results,)) process.start() list_processes.append(process) [process.join() for process in list_processes] print map_results.values()
メリット • シリアライズ出来なくても使える
デメリット • 前の2つよりは面倒
• 例外処理が面倒
その他multiprocessing Tips
• インスタンスメソッドはちょっと工夫するとPool.map()が使えます
http://aflc.hatenablog.com/entry/20110825/1314366131
• multiprocessingはプロセス作成のオーバーヘッドが大きいので
ひとつの処理が数十秒程度かかる処理で使用した方が良い
2015年4月3日 PyData.Tokyo Meetup #4 34
その他、何かあればお願いします
GPGPU(General-purpose computing on graphics processing units)
基本的にGPGPUはCUDAでプログラミング
2015年4月3日 PyData.Tokyo Meetup #4 35
GPUの演算資源を画像処理以外の目的に応用する技術 by wikipedia
GPGPU
CUDA NVIDIAが提供するGPU向けのC言語の統合開発環境 by wikipedia
注意:データサイエンティストには障壁が高いかも・・・
CUDAのしくみ
GPUデバイス上で実行するカーネル関数を定義して、それを各スレッドで実行
2015年4月3日 PyData.Tokyo Meetup #4 36
1つのスレッドで 1つのカーネル関数を実行
1ブロックで
512個のスレッドを実行
1グリッドで、x軸y軸ともに
65535個のブロックを実行
65535×65535×512個のスレッド実行を一度に命令できる
引用: CUDAプログラミングモデルの概要(http://http.download.nvidia.com/developer/cuda/jp/CUDA_Programming_Model_jp.pdf)
Numbaでのカーネル関数記述
2015年4月3日 PyData.Tokyo Meetup #4 37
PythonにJITコンパイラを導入して高速化するモジュール ※後述
GPUも扱える。 Numba
from numba import cuda import numpy @cuda.jit('void(f8[:, :], f8[:, :])') def pairwise_distance(X, D): '''距離行列計算関数 ''' M = X.shape[0] N = X.shape[1] #スレッドとブロックのインデックスを使って #このスレッドで計算する地点ペアを特定 i, j = cuda.grid(2) if i < M and j < M: d = 0.0 for k in range(N): tmp = X[i, k] - X[j, k] d += tmp * tmp D[i, j] = math.sqrt(d) if __name__ == '__main__': X = numpy.random.random((1000, 3)) # 3次元座標 D = numpy.empty((1000, 1000)) # 出力される距離行列 griddim = 100, 100 # 100*100にブロックを配置 blockdim = 16, 16 # ブロック内で16*16にスレッドを配置 pairwise_distance[griddim, blockdim](X, D)
STEP2
1600×1600のスレッドの場所で
どの2地点間の計算するか決定 ※この設計だと1600以上の地点間は計算出来ない
STEP1
1600×1600のスレッドを用意
C言語での記述とほぼ同様のフォーマット
Numba(GPGPU) Tips
2015年4月3日 PyData.Tokyo Meetup #4 38
• 基本的にはCPUよりGPUの方が速い – ただし、GPUへのメモリ転送のオーバーヘッドと
GPUメモリが少ないことから、CPUが優ることも多い
• メモリチューンは高速化の道だが茨の道 – 同一ブロック内でアクセスできるシェアードメモリを使うと、さらなる高速化
– コレの実装はすごい面倒なので、データサイエンティストはやらなくても・・・
• ライブラリを使いましょう – カーネル関数の自前実装は疲れるのでcuBLAS, cuFFT, cuRANDなどの
既存ライブラリを使いましょう
– Numbaの有料版のNumbaProでは使えます
• 詳細はブログに書きました – 『Python高速化 Numba入門』
http://yutori-datascience.hatenablog.com/entry/2014/12/09/235628
言語レベルの高速化
2015年4月3日 PyData.Tokyo Meetup #4 39
ハードウェア レベル
ソフトウェア レベル
コーディングレベル
Hadoop Streaming
Spark
H2o (機械学習限定)
クラスタレベル
リスト内包表記 辞書型の活用
Numpy等の利用 疎行列の利用
(番外編)プロファイラの利用
コンピュータレベル
マルチプロセス計算 (multiprocessing)
GPGPU (Numba)
言語レベル
C拡張(Cython)
JITコンパイル(PyPy, Numba)
BLAS最適化
Pythonは何でこんなに遅いのか
主に以下の3つが原因
2015年4月3日 PyData.Tokyo Meetup #4 40
1. GILのせい ※前述
2. 毎回の型チェック (動的型付け言語のため)
3. 配列がない (配列でなくリスト)
とりあえずの解決策
Numpyの配列使用とJITコンパイルで型チェックを減らす
2015年4月3日 PyData.Tokyo Meetup #4 42
PythonにJITコンパイラを導入して高速化するモジュール ※再掲 Numba
Pythonライクの記法でC拡張モジュールを生成する Cython
今日は以下の3つを紹介
自動生成で、静的型付けと配列のあるC言語を使う
行列演算などの基本の数値計算を予め高速化しておく BLAS最適化
Numpyの裏側をさらに高速化
Numba(CPU)
基本的にはデコレータ一発
サポート外のpythonオブジェクトが無ければnopython=Trueでさらに高速化
2015年4月3日 PyData.Tokyo Meetup #4 43
@jit('f8[:, :](f8[:, :], f8[:, :])', nopython=True) def pairwise_distance(X, D): M = X.shape[0] N = X.shape[1] for i in range(M): for j in range(M): d = 0.0 for k in range(N): tmp = X[i, k] - X[j, k] d += tmp * tmp D[i, j] = numpy.sqrt(d) return D
普通のPython 4.69秒
Numba 0.015秒
scipyのpdist 0.007秒
3次元座標の1,000地点間の距離計算時間はこちら
上の実装は、対称行列分の半分はサボれるので納得の結果
Cython
3倍速いけど、あんまり速くない。不味いところあったら教えて下さい
2015年4月3日 PyData.Tokyo Meetup #4 44
import numpy cimport numpy def pairwise_distance(numpy.ndarray[double, ndim=2] X, numpy.ndarray[double, ndim=2] D): cdef int i, j, k, M, N cdef double d, tmp M = X.shape[0] N = X.shape[1] for i in xrange(M): for j in xrange(M): d = 0.0 for k in xrange(N): tmp = X[i, k] - X[j, k] d += tmp * tmp D[i, j] = numpy.sqrt(d) return D
C言語っぽく型を、ひたすら固定 Numbaよりは若干面倒
*.pyxで保存して、import pyximport; pyximport.install()で使う
普通のPython 4.69秒
Cython 1.22秒
3次元座標の1,000地点間の距離計算時間はこちら
BLAS最適化
数値計算ならコレで結構速くなる 2015年4月3日 PyData.Tokyo Meetup #4 45
数値計算をやると裏で必ずお世話になるのがBLASとLAPACK
Basic Linear Algebra Subprograms (BLAS) -線型代数計算を実行するライブラリの標準仕様
Linear Algebra PACKage (LAPACK) -BLAS上に構築された固有値計算などの高位な線形代数計算ライブラリ
現在様々なBLAS実装が公開されている
Intel MKL … MATLABはコレ 有償 すごく速い・高い・安心!
ATLAS … 自動チューンのBLAS BSD 速い
GotoBLAS2 … 後藤和茂氏作成のBLAS BSD かなり速い 開発停止
OpenBLAS … xianyi氏によるGotoBLAS2の後継BLAS BSD すごく速い
(MATLAB, R, Octave, numpy …)
・・・
BLASの比較
2015年4月3日 PyData.Tokyo Meetup #4 46
引用:R BLAS: GotoBLAS2 vs OpenBLAS vs MKL (http://blog.felixriedel.com/2012/11/r-blas-gotoblas2-vs-openblas-vs-mkl/)
実行コード
A = matrix(rnorm(n*n),n,n)
A %*% A
solve(A)
svd(A)
RのデフォルトBLASから何倍早くなったか検証してるサイトがあったので紹介
最大で11倍ほど高速化
MKLが基本的に一番高速
OpenBLASも所によってはMKLを上回ることも
マルチスレッド環境では導入は必須かも
コーディングレベルの高速化
2015年4月3日 PyData.Tokyo Meetup #4 47
ハードウェア レベル
ソフトウェア レベル
Hadoop Streaming
Spark
H2o (機械学習限定)
クラスタレベル
コンピュータレベル
マルチプロセス計算 (multiprocessing)
GPGPU (Numba)
言語レベル
C拡張(Cython)
JITコンパイル(PyPy, Numba)
BLAS最適化
コーディングレベル
リスト内包表記 辞書型の活用
Numpy等の利用 疎行列の利用
(番外編)プロファイラの利用
リスト内包表記
多くの方がご存知の通り、リスト内包表記は早いです
2015年4月3日 PyData.Tokyo Meetup #4 49
list_results = [] for i in xrange(1000000): list_results.append(str(i))
list_results = [] for i in xrange(1000000): list_results += [str(i)]
list_results = [str(i) for i in xrange(100000)]
リストの足し算するよりも、
appendしたほうが速く
さらに、リスト内包表記の方が速いです
リスト内包表記は『内部的にはappendをせずに、直接リストにぶち込めるから』速いのだそうです。 『リスト内包表記はなぜ速い?』 http://dsas.blog.klab.org/archives/51742727.html
0.42秒
0.35秒
0.26秒
list_results = [] list_results_append = list_results.append for i in xrange(1000000): list_results_append(str(i))
さらに、appendのdotを外したほうが速く
0.32秒
list_results = map(str, xrange(100000))
実は、今回の場合はmapが一番速いです。(mapはlambda式とか使うと遅い)
0.13秒
辞書型の活用
データがあるかどうかの探索は、計算時間にかなり効果的
配列を全て走査する線形探索O(n)を理由なくやるのはご法度
2015年4月3日 PyData.Tokyo Meetup #4 50
list_elements = [i for i in xrange(10000)] for i in xrange(10000): if i in list_elements: pass
0.87秒
リストに対するin演算は線形探索だが、
辞書に対するin演算はハッシュ探索なのでO(1)で動作
map_elements = {i: None for i in xrange(10000)} for i in xrange(10000): if i in map_elements: pass
0.001秒
気を抜くと、やりがちなので注意が必要 ※状況に応じて二分木探索も
(番外編)最近傍探索など
たまに問題になる、最近傍探索
2015年4月3日 PyData.Tokyo Meetup #4 51
距離空間における最も近い点を探す最適化問題の一種 by wikipedia
最近傍探索
類似度計算などで、誰が一番近いかを探すのに使用
厳密に解く
・ scikit-learnのNearestNeighbor (Ball treeとKD tree)
近似的に解く
・ LSH (Locality Sensitive Hashing)
有名ドコロのPythonモジュールには実装なし
Numpy等の利用
2015年4月3日 PyData.Tokyo Meetup #4 54
Python数値計算はfor文を書いたら負け
行列演算に落としてNumpyを利用
多少見た目が非効率でも行列演算に落とした方が速い
全てnumpyの行列演算だけでやる気持ちで行きましょう
Numpy等の利用
行列積とか今更比べても仕様がないので、私の失敗例
2015年4月3日 PyData.Tokyo Meetup #4 55
pandasのデータフレームに対して多対一の距離計算を考えていて
user_all_vecs = numpy.random.random((100000, 100)) # ユーザ毎のベクトル(多) some_user_vec = numpy.random.random(100) # あるユーザのベクトル(一) pd_user_all_vecs = pandas.DataFrame(user_all_vecs) ) # pandas準備
インデックスを保持したいので行ごとにapplyで計算すると超遅く
scipyのcdistと再インデックス付与の方が断然速い
pd_user_all_vecs.apply(lambda vec: euclidean(vec, some_user_vec), axis=1)
pandas.Series(cdist(pd_user_all_vecs,[some_user_vec])[:, 0], index=pd_user_all_vecs.index)
3.9秒
0.06秒
BLASパワーはマジで偉大
疎行列の利用
2015年4月3日 PyData.Tokyo Meetup #4 57
世の中のものは大体が疎(スパース)
東京タワーに鉄骨はたくさんあるが、
ひとつの鉄骨には数個のみ接続
世界人口は70億人いるが、
ひとりの人間の友人数は数百人程度
scipy.sparseの利用
コイツで疎行列でやりたことは大体出来る。 (かなりのレベルで揃っている)
scikit-learnもscipy.sparseの疎行列に結構対応済 2015年4月3日 PyData.Tokyo Meetup #4 59
http://docs.scipy.org/doc/scipy/reference/sparse.html
scipy.sparseの利用
2015年4月3日 PyData.Tokyo Meetup #4 60
色々提案されているので、主要な疎行列表現のみ紹介
COO表現 (COOdinate format)
行番号、列番号、要素の3配列で行列を表現
>>> row = numpy.array([0, 3, 1, 0]) >>> col = numpy.array([0, 3, 1, 2]) >>> data = numpy.array([4, 5, 7, 9]) >>> coo_matrix((data, (row, col)), shape=(4, 4)).toarray() array([[4, 0, 9, 0], [0, 7, 0, 0], [0, 0, 0, 0], [0, 0, 0, 5]])
メリット • 超簡単
デメリット
• 要素のアクセスが線形探索のみ(ソート済みなら二分木探索)
• 要素の追加が難しい
• この表現のまま行列演算は基本難しい
使用例
http://docs.scipy.org/doc/scipy/reference/generated/scipy.sparse.csc_matrix.html#scipy.sparse.coo_matrix
scipy.sparseの利用
2015年4月3日 PyData.Tokyo Meetup #4 61
CSR表現 (Compressed Sparse Row format)
列番号、行の境目、要素の3配列で行列を表現
>>> indptr = numpy.array([0, 2, 3, 6]) >>> indices = numpy.array([0, 2, 2, 0, 1, 2]) >>> data = numpy.array([1, 2, 3, 4, 5, 6]) >>> csr_matrix((data, indices, indptr), shape=(3, 3)).toarray() array([[1, 0, 2], [0, 0, 3], [4, 5, 6]])
使用例
メリット • 行列演算がしやすい
• 行要素が定数個と仮定するならO(1)で要素アクセス可能
デメリット • 作るのが面倒くさい
• 要素アクセスはO(1)だとしても面倒
http://docs.scipy.org/doc/scipy/reference/generated/scipy.sparse.csc_matrix.html#scipy.sparse.csr_matrix
scipy.sparseの利用
2015年4月3日 PyData.Tokyo Meetup #4 62
CSC表現 (Compressed Sparse Column format)
行番号、列の境目、要素の3配列で行列を表現
使用例
メリット • 行列演算がしやすい
• 行要素が定数個ならO(1)で要素アクセス可能
デメリット • 作るのが面倒くさい
• 要素アクセスもO(1)だとしても面倒
http://docs.scipy.org/doc/scipy/reference/generated/scipy.sparse.csc_matrix.html#scipy.sparse.csc_matrix
>>> indptr = np.array([0, 2, 3, 6]) >>> indices = np.array([0, 2, 2, 0, 1, 2]) >>> data = np.array([1, 2, 3, 4, 5, 6]) >>> csc_matrix((data, indices, indptr), shape=(3, 3)).toarray() array([[1, 0, 4], [0, 0, 5], [2, 3, 6]])
行と列が入れ替わっただけなので、
CSR表現と同じ
scipy.sparseの利用
2015年4月3日 PyData.Tokyo Meetup #4 63
このへんはscipy.sparse特有の表現
LIL表現 (linked list sparse format)
各行毎に、列番号リストと要素リストで行列を表現
リストが標準のPythonならでは。性能はどれも平均的
dok表現 (Dictionary Of Keys based
sparse format)
行番号と列番号がキーになった辞書で行列を表現
O(1)で要素にアクセス可能だが、行列演算は厳しい
要素の上書きが必要な場合に威力を発揮
※要素アクセスは辞書型に比べ遅いので、行列にこだわらなければ辞書型で
疎行列演算Tips
行列ベクトル積で完結させる
行列ベクトル積はCSRとCSC表現なら簡単に計算可能
行列行列積は基本は密行列なってしまう
逆行列も基本密行列
線形方程式を解きたいときは反復解法(Krylov部分空間法)
直接解法の疎LU分解も無くはないが・・・
2015年4月3日 PyData.Tokyo Meetup #4 64
(番外編)プロファイラの利用
この3つを紹介 2015年4月3日 PyData.Tokyo Meetup #4 66
ここまで、高速化の方法を説明してきましたが、
IPythonの%timeと%timeitを使う
Loggingをちゃんと使う
プロファイラの利用
どの処理がボトルネックか判断するのが大事
IPythonの%timeと%timeitを使う
IPythonには時間計測に%timeと%timeitのマジックコマンドが存在
2015年4月3日 PyData.Tokyo Meetup #4 67
In [5]: %time sum(i for i in xrange(10000)) Wall time: 1 ms Out[5]: 49995000 In [6]: %timeit sum(i for i in xrange(10000)) 1000 loops, best of 3: 330 us per loop
%timeitは繰り返し解いて、信頼度の高い計算時間を返してくれる
Loggingをちゃんと使う
概算でよいなら、loggingでログを吐くのがオススメ メソッド前と後ろでログ吐けば大体の時間はわかる
2015年4月3日 PyData.Tokyo Meetup #4 68
2015-03-23 00:32:53,385 ga_aa_optimize.optimize 50 [INFO]start optimize with image_num=20, max_iter=50 2015-03-23 00:32:53,387 ga_aa_optimize.optimize 51 [INFO]start to make initial solutions. 2015-03-23 00:32:55,530 ga_aa_optimize.optimize 81 [INFO]row_shift is 0. 2015-03-23 00:32:55,530 ga_aa_optimize.optimize 82 [INFO]end making initial solutions in 2.14299988747 seconds. 2015-03-23 00:32:57,713 ga_aa_optimize.optimize 135 [INFO]1: 58517.0 | 2.17900013924 2015-03-23 00:33:00,621 ga_aa_optimize.optimize 135 [INFO]2: 57258.0 | 2.90799999237 2015-03-23 00:33:03,428 ga_aa_optimize.optimize 135 [INFO]3: 56428.0 | 2.80799984932 2015-03-23 00:33:06,232 ga_aa_optimize.optimize 135 [INFO]4: 56242.0 | 2.80300021172 2015-03-23 00:33:08,980 ga_aa_optimize.optimize 135 [INFO]5: 56173.0 | 2.74699997902 2015-03-23 00:33:11,894 ga_aa_optimize.optimize 135 [INFO]6: 56004.0 | 2.91499996185 2015-03-23 00:33:14,677 ga_aa_optimize.optimize 135 [INFO]7: 55948.0 | 2.78200006485 2015-03-23 00:33:17,608 ga_aa_optimize.optimize 135 [INFO]8: 55919.0 | 2.93099999428 2015-03-23 00:33:20,421 ga_aa_optimize.optimize 135 [INFO]9: 55919.0 | 2.81200003624 2015-03-23 00:33:23,270 ga_aa_optimize.optimize 135 [INFO]10: 55899.0 | 2.84799981117
http://qiita.com/amedama/items/b856b2f30c2f38665701
『ログってなに?printでいいだろ』って人はここを読むと良いかも
プロファイラの利用
ここまでは、1 callの時間を解析
プロファイラは関数毎の積算計算時間を解析可能
2015年4月3日 PyData.Tokyo Meetup #4 69
プロファイラ(性能解析)
ソフトウェア工学における性能解析または性能分析(英: Performance analysis)とは、動的プログラム解析の一種であり、プログラムの実行を通して情報を収集することでプログラムの性能を解析することを言う。逆にプログラムを実行せずに行う解析を静的コード解析と呼ぶ。性能解析の目的は、実行時間やメモリ使用量を最適化するためにプログラムのどの部分を改良すべきかを決定することである(ボトルネック、アムダールの法則参照)。
(引用: Wikipedia)
プロファイラの利用
2015年4月3日 PyData.Tokyo Meetup #4 70
11711673 function calls (11703283 primitive calls) in 38.377 seconds Ordered by: internal time ncalls tottime percall cumtime percall filename:lineno(function) 310291 18.719 0.000 18.719 0.000 ga_aa_optimize.py:246(<dictcomp>) 3900 4.525 0.001 26.568 0.007 ga_aa_optimize.py:221(_row_local_search) 100 3.393 0.034 5.959 0.060 ga_aa_optimize.py:100(generate_image) 200 3.186 0.016 3.330 0.017 ga_aa_optimize.py:162(calc_objective) 310565 2.762 0.000 3.118 0.000 {sorted} 100 1.323 0.013 27.891 0.279 ga_aa_optimize.py:194(local_search) 7777 1.055 0.000 1.055 0.000 {numpy.core.multiarray.concatenate} 310314 0.502 0.000 0.502 0.000 {numpy.core.multiarray.empty} 6559368 0.355 0.000 0.355 0.000 ga_aa_optimize.py:250(<lambda>) 310309 0.257 0.000 0.308 0.000 random.py:273(choice)
python -m cProfile -s time *.py 引数 使用方法
結果
意味 ncalls :呼び出し回数 tottime :この関数が消費した時間の合計 percall : tottime を ncalls で割った値 cumtime :下位の関数を含むこの関数 消費時間の合計。 filename:lineno(function) その関数のファイル名、行番号、関数名
基本tottimeが大きい関数を高速化すればOK