【読書メモ】ハイパフォーマンスPython 1、2章

最近なぜかパフォーマンスチューニングに興味があり、この本を読んでいる。

1章 高性能なPythonを理解する

1章ではパフォーマンスチューニングにおいて知っておくべきコンピュータアーキテクチャの基礎と、それに関わるPythonについて書いてある

コンピュータシステムの基礎

コンピューターは簡単に言うと、「演算装置(ALU)」「記憶装置(RAM,ROM)」「両者の接続装置(BUS)」から構成されている

演算装置は、ビット列を入力とし、変換し、別のビット列を出力する関数のようなものと考えることができる。

演算装置の特性としては、「1サイクルあたりに処理できる命令数(IPC)」と「1秒間あたりに処理できる数(クロック周波数)」の2つがある。IntelのCoreシリーズはIPCに優れているがクロックは弱い。一方PentiumシリーズはIPCは弱いがクロックは強い。

複数のデータを1度に入力し、計算することをベクトル化と呼ぶ。ベクトル化を行うとIPCが上がる。

ここ10年ではIPCやクロック周波数は頭打ちになってきている。そこでチップメーカーはハイパースレッディング(2つの命令を1つのCPUで実行する)やアウトオブオーダー(実行順序に依存しない処理を最適化する)など工夫をしている。

アムダールの法則とはいくら複数コアで処理を並列化しても単一コアでしか処理できない部分がボトルネックになってしまうこと。Pythonの場合であれば、グローバルインタプリタロックによって複数スレッドで実行しても1度に実行できるのは1つの命令のみであり、性能がわるい。(マルチプロセスなら関係ない?)

記憶装置については、多数のちいさなデータよりも大きな1つのデータを扱う方が効率が良い。一般的に記憶装置の速度と容量は反比例の関係にあり、L1,L2キャッシュ、RAM、ROMのように多層化することで速度を確保している。データを見つけるまでのレイテンシも重要。

接続装置については、データ転送のスピードが重要。L1、L2キャッシュと接続しているバスは速い。GPUなどの周辺デバイスへのアクセスは遅い。特性としては、「1回にどれだけの量を転送できるか(バス幅)」と「1秒間に何回転送できるか(バス周波数)」。ベクトル化されていると、1回に転送する量が増えるので早くなる。

パフォーマンスとしてのPython

Pythonのインタプリタはコンピュータアーキテクチャの底レイヤな部分を高度に抽象化する。これはアイデアの実装スピードとのトレードオフなので、Pythonの実装の楽さを求めるなら性能は落ちる。

普通にPythonを使っているだけではベクトル化は難しい。numpyなどのツールを使う必要がある。

Pythonはガベージコレクションを備えた言語なのでヒープの自由な位置にメモリを確保し、破棄されるタイミングはGCによる。よってメモリの断片化が生じ、メモリの最適化が難しくなる。

gccのコンパイラはコンパイルすることによって最適化処理をする。がPythonはコンパイルしないし、動的型付けなので実行時まで処理がわからない。なので最適化が難しい。

それでも、Pythonを使うのはCで書かれた安定したライブラリが多く存在するから。そして、アイデアを即時にプロトタイピングできること。

2章 ボトルネック発見のためのプロファイリング

ジュリア集合

プロファイリングのサンプルコードとしてジュリア集合のコードがあるがはっきりいってジュリア集合の理論はわからなかった。サンプルコードは以下にある。(Python2)

printとデコレータ

普通にimport timeしてtime.time()で時間を計測しましょうという話。いちいち計算がめんどかったらデコレータを書きましょう

Pythonのtimeitモジュールを使うとn回のループをr回反復し、かかった時間を計測してくれる。

python -m timeit -n 5 -r 4 -s "print \"hoge\""

timeコマンド

おなじみunixのtimeコマンドで楽に全体の実行時間を計測できる。結果はreal(経過時間)とuser(カーネル関数以外)とsys(カーネル関数)で表示されるがreal-(user+sys)でどれだけIO待ちがあったかがわかる。vオプションでいろいろ出てくるがpage faultの回数もみることができる。page faultが増えると実行時間がおそくなるぞ。

cProfileモジュール

python組み込みのプロファイラ。各関数にかかった時間と呼び出し回数、単位回数あたりにかかった時間が表示される。runsnakerunというモジュールを使うとcProfileの結果を可視化できる。

line_profiler

サードパーティモジュール。行ごとの実行時間を計測してくれる。めっちゃ実行時間がかかる。

Pythonは式を基本左から右に評価するので式の左側に計算コストの低い処理を置くのは合理的

memory_profiler

サードパーティ製メモリプロファイラ。行単位でメモリ使用量を表示してくれる。mprofというユーティリティを使うと結果をグラフ化できる。

heapy

処理の途中に挟むことでヒープの使用量を表示してくれる。guppyパッケージに入っている。

dowser

変数の生成状況を可視化してくれる。Webブラウザ上でみる。

dis

Pythonの各処理で生成される低レベルなコードを表示してくれる。

自前で関数を書くより、組み込み関数を使った方が圧倒的に速い。

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.