# NumPyの歴史とPythonの並行処理(PyDataTokyo Conference) https://pydatatokyo.connpass.com/event/87511/ >Sessions Session1 - データ分析のための Python パフォーマンスチューニングテクニック スピーカー:石本 敦夫 様 > >概要: Python黎明期からはじまる数値演算ツールの発展史を簡単に紹介するとともに、NumPyなどの数値演算パッケージで効率的にCPU・GPUを利用するテクニックを解説します 現在フリーのプログラマ python.jp(ドキュメントサイト)管理人 パーフェクトPython、文法紹介 ## PythonとNumPyの歴史 現状:機械学習でPythonがサジェストされる 2000年くらいにはPythonは数値計算に強いという地位を確立 2010年にはライブラリが充実 1994年 Python1.0 90年代にPILやNumPyの前身(Numeric) ←GitHubなどない暗黒時代 2001年くらいにEnthought(Anacondaと同種のプロダクト):数値計算のコミュニティで盛り上がり 現在使われているものは2010年までには出揃っている(pandas, scikit-learn) 2010年代前半 Anaconda なぜPythonが使われるようになったのか? - NumPyが早い時期に誕生。NumPyがエコシステムを形成 - PythonもNumPyを呼び出す前提で開発 - C拡張ライブラリの作りやすさ ### NumPy エコシステム NumPyはGuideも入って標準パッケージ前提で開発された。 当初から「公式」と認識されていた。ライブラリ乱立せず ### Pythonからのサポート 言語仕様としてNumPyが使いやすくなるようにしてきた `L[i][j][k]`を`L[i, j, k]`と書けるように(Guideの提案) Ellipsis `L[1:2, ..., 2:3]`:NumPy専用機能 PEP 203 `A *= 2`という書き方(Python2.0〜)はNumPyの速度向上のため PEP 207 numpyで要素ごとのTrue/Falseを返せるように(Python2.1) PEP 357 Python2.5 np.int32(1)でもインデックス指定できる PEP 3118 ndarrayのフォーマット(行列表現)はPythonの言語仕様として定義 PEP 465 行列積用の演算子`@`の追加(Python3.5) **PythonはNumPyが使いやすくなるように開発されてきた** ### C拡張の作りやすさ PythonスクリプトをそのままCで書けるAPI(安定。古いものも動く) Cython 2010年以降の機械学習ブームもあってPythonの人気が上昇 StackOverflowの記事数10%を占めるように(JSを抜いた) python.jpもアクセス数3倍 ## データ分析処理の並行化 こちらが本題 「遅いんでマルチスレッド、マルチプロセスにして早くしたい」「しても早くならない」という質問 ### Pythonの並行化 マルチスレッドにしたくらいで早くなったら苦労はない(そんなに期待してはいけない。3-4倍になることはあまりない) **基本は計算量の削減、データ量の削減** `concurrent.futures`(Python3.2〜) threadingやmultiprocessingより便利 マルチスレッドとマルチプロセス切り替え簡単 マルチスレッドとマルチプロセスどちらを使うか? **データ分析ならマルチスレッド** マルチプロセスはプロセス間のデータ共有など複雑(プラットフォーム依存) Gloval Interpreter Lock (GIL) ギル 2つのスレッドが同時にPythonインタプリタの処理を実行できない いくつ立ち上げても動いているのは1つだけ:管理の仕組みがGIL 5msでスレッド実行し、次のスレッドへ スレッドを使う側からは同時に動いてほしい。無意味なのか? 処理にかかる時間はスレッドを使っても使わなくても同じ(パフォーマンスは向上しない)←Pythonの機能だけを使った場合 GILを使う意味は、Pythonの内部機能を使わない処理は同時に動いてもかまわないということ 例:ループで足し算の処理はGILが必要、print()処理はGILは不要(コンソール出力はOSが仕事をしている) **他のスレッドで計算している間に実行できる部分がある**(OSの機能を使う部分) ネットワーク、ファイルの入出力はPythonの内部機能ではないので、スレッドの効果を得られる ### Numpyの並行化 行列同士の足し算 `numpy.add()` 10×10, 100×100:マルチスレッド、マルチプロセスどちらも成果はない(Pythonのオーバーヘッドが大きい) 1000×1000:マルチスレッド、マルチプロセスの効果が見られる 並行化してもそんなに早くなるものではない(オーバーヘッドが発生する) 大きいデータで計算時間が長い場合、4スレッド/プロセスまでは効果が見られる。GPUの性能がよくてもメモリの読み込み・書き込みが追いつかなくなる(データサイエンスでメモリを考えるのは現実的でない) NumPyはこまめにGILを解放している `numpy.dot`は効果が出ないケース(逆に遅くなる) numpyが裏でマルチスレッド化している(自分でマルチスレッド化するのは余計なことをしている) 試してみないとわからない ### GPUの並行化 CPUとGPUの通信:ストリーム CPUから命令を転送し、GPUで実行する CPUは命令の結果を待たずに次の命令をGPUに投げる:非同期実行 CPUだけでの実行は同期実行(計算が終わってから次へ) GPUの計算は非同期だが、データ転送は同期的(計算が終わるまでブロック) データ転送と演算の繰り返しになるが、**複数ストリームにして一方でデータ転送している間に演算する**「オーバーラップする」(←パイプライン思い出した) PyTorchで複数ストリームの例 演算を行っている間に次のデータ転送をするようにプログラムを組む(3-4割上がればいい方) 可視化ツール:Nvidia Visual Profiler 検索した:https://qiita.com/Hiroki11x/items/3737e4e267c1035a4b55 マルチGPUのケース それぞれのGPUが独立してストリームを持つ(独立してデータ転送) device(0)でa+b(転送して演算)、device(1)でc+d オーバーラップ実現 NVLink環境(CPU−GPUの転送帯域が広い) マルチGPUではthreadが利く データ転送と演算が完全に並行になる **複数のGPU環境ではスレッドを使い、同じデータにハイパーパラメタを変えて実行するテクニック**