1. hoto17296

    No comment

    hoto17296
Changes in body
Source | HTML | Preview
@@ -1,99 +1,102 @@
些細なことでも [multiprocessing](https://docs.python.org/ja/3/library/multiprocessing.html) 使うと便利だよ、という小ネタ。
## やりたいこと
``` python
from glob import glob
files = glob('data/*.csv')
len(files) # 10000
```
この 1万件の CSV ファイルを Pandas DataFrame として読み込みたい。
ちなみに検証用のデータは以下のようにして生成した。
(3列 x 10,000行 の CSV ファイル 10,000 個)
``` python
import numpy as np
import pandas as pd
row_n = 10000
col_n = 3
columns = [f'col{i}' for i in range(col_n)]
for i in range(10000):
df = pd.DataFrame(np.random.randn(row_n, col_n), columns=columns)
df.to_csv(f'data/{i:04}.csv', index=False)
```
## 並列化の手法いくつか
今回は
- 並列化なし (シングルスレッド)
- マルチスレッド
- マルチプロセス
で比較した。
-asyncio (イベントループ) については、[asyncio がファイルの非同期読み込みをサポートしていなかった](https://github.com/python/asyncio/wiki/ThirdParty#filesystem)ので試さなかった。 aiofiles などの外部モジュールを使えばできると思うので、<del>誰か試してみてほしい。</del> [追記] 試してみた → [コメント](https://qiita.com/hoto17296/items/586dc01aee69cd4915cc#comment-631a1ed95516c5186de6)を参照。
+asyncio (イベントループ) については、[asyncio がファイルの非同期読み込みをサポートしていなかった](https://github.com/python/asyncio/wiki/ThirdParty#filesystem)ので試さなかった。 aiofiles などの外部モジュールを使えばできると思うので、<del>誰か試してみてほしい。</del> [追記] 試してみた → [コメント](#comment-631a1ed95516c5186de6)を参照。
### 並列化なし (シングルスレッド)
並列化を考えずに全てのファイルを読み込もうとするとこうなる。
``` python
import pandas as pd
arr = [pd.read_csv(f) for f in files]
```
### マルチプロセス
[multiprocessing.Pool](https://docs.python.org/3/library/multiprocessing.html#multiprocessing.pool.Pool) を使う。
``` python
import pandas as pd
from multiprocessing import Pool
def read_csv(f):
return pd.read_csv(f)
with Pool() as p:
arr = p.map(read_csv, files)
```
`p.map` には `pd.read_csv` を直接渡したり `lambda` を渡したりすることはできないので、 `read_csv` という関数を定義している。
`Pool()` で並列数を指定しなかった場合は CPU のコア数と同じ並列数で実行される。
### マルチスレッド
[multiprocessing.dummy.Pool](https://docs.python.org/3/library/multiprocessing.html#module-multiprocessing.dummy) は threading を使っているとのことなので、これを使ってみる。
``` python
import pandas as pd
from multiprocessing.dummy import Pool
with Pool() as p:
arr = p.map(pd.read_csv, files)
```
マルチスレッドの場合は `pd.read_csv` をそのまま渡せる。
## 結果
4コア CPU のマシンで計測した結果が以下。
|| 読み込み速度 |
|---|---|
| 並列化なし (シングルスレッド) | 82 sec |
| マルチスレッド | 28 sec |
| マルチプロセス | 22 sec |
マルチプロセスが一番速かった。
4並列で読み込んで4倍速くなるとまではいかなかったが、並列化の恩恵を十分に感じられる程度には速くなった。
### マルチスレッドの注意点
今回はマルチスレッドもマルチプロセスとほぼ同じくらいには速くなったので、手軽さを重視してマルチスレッドの方を採用してもいいケースもあるかもしれない。
ただし Python (CPython) のマルチスレッドは [GIL (Global Interpreter Lock)](https://wiki.python.org/moin/GlobalInterpreterLock) の制約があり、複数のスレッドが同時に Python バイトコードを実行することができない。
-そのため、今回のファイル読み込みのような I/O バウンドな処理は高速化できるかもしれないが、例えば「読み込んだ各データに対してなんらかの重い処理を行う」などの CPU バウンドな処理を併せて実行した場合はおそらく今回ほど速くならない。そのようなケースではマルチプロセスで並列化した方が高速化できると思われる。
+<del>そのため、今回のファイル読み込みのような I/O バウンドな処理は高速化できるかもしれないが、例えば「読み込んだ各データに対してなんらかの重い処理を行う」などの CPU バウンドな処理を併せて実行した場合はおそらく今回ほど速くならない。そのようなケースではマルチプロセスで並列化した方が高速化できると思われる。</del>
+
+違う気がしてきた・・・。
+詳しくは[コメント](#comment-c65a6ce1f11809eddeb3)を参照。