Mizu – 共有メモリ上のゼロコピー並列RおよびPython
Mizu – zero-copy parallel R and Python over shared memory (github.com)
要約
Mizuは、PythonとRの間で共有メモリを介したゼロコピーの並列処理を実現するライブラリです。ソケット通信と比較してタスクのラウンドトリップ時間を大幅に短縮し、GILの制約を受けるPythonではプロセスベースの並列処理を可能にします。Numpy配列やArrowテーブルなどのデータ構造をコピーなしで共有でき、asyncioやconcurrent.futuresとの互換性も提供します。
全文翻訳
pymizu
[画像: PyPI] [画像: ci] [画像: codecov] [画像: License: MIT] [画像: Ruff] [画像: Python 3.10+]
共有メモリを介したスレッド速度の非同期および並列Python。
タスクのラウンドトリップは、ソケット経由の約100マイクロ秒から1マイクロ秒未満に短縮されるため、スレッドで行うのと同じくらい細かく作業を分割できます(ベンチマークを参照)。リスト、辞書、Numpy配列、Arrowテーブルなどの一般的なデータ構造は、PythonとRの間でゼロコピービューとして共有できます。
デフォルトのCPythonビルドでは、GILはCPUバウンドなスレッドを一度に1コアで実行するため、Pythonでの計算並列処理は通常、複数のプロセスを意味します。プロセスは障害の分離も行います。ワーカーがクラッシュしても、ホストがダウンすることはありません。
チャネルとワークスティーリングタスクプールは、POSIX共有メモリ(Linux、macOS)またはWin32ファイルマッピング(Windows)上で実行されます。ロックフリーのシングルプロデューサーシングルコンシューマーリング(バッチ公開付き)、ホットパスでカーネルに入らないスピン・パーク待機、ピアが死亡したときのOS通知を備えています。
pymizuは、ロックフリー共有メモリIPC用のCライブラリであるlibmizu上に構築されています。mizuは同じコアのバインディングをR用に提供するため、PythonとRは互いに通信できます。
- ゼロコピーNumpy配列およびArrowの相互運用(pyarrow、polars、pandas、duckdb)
- ドロップイン
concurrent.futures.Executor— 実際のFuture、wait、as_completed、asyncio.wrap_future - イベント駆動型asyncio — チャネルでのタスクとマップの
await、async forおよびrecv_async()— データ到着時のウェイクアップ、ポーリングスレッドなし - 再現可能な並列乱数(
seed=) — 任意のワーカー数またはスティール順序で同一の結果 - ネストされた並列マップ — タスクは独自のプールでマップでき、デッドロックフリー
ライフサイクル: 実験的。 APIはまだ安定しておらず、フィードバックに応じて変更を行う可能性があります。
インストール
PyPIからインストールします。
pip install pymizu
またはGitHubから開発版をインストールします。
pip install git+https://github.com/shikokuchuo/pymizu
64ビットプラットフォーム上のPython 3.10以降が必要です。拡張機能はバンドルされたCコアをコンパイルするため、システムライブラリは不要です。
オプションの追加機能:
pymizu[numpy]: ゼロコピー配列ビューおよびローティア配列ステージングpymizu[cloudpickle]: ラムダ、クロージャ、ローカル関数をプールタスクとして使用
GitHubのインストールで追加機能をリクエストするには、pip install "pymizu[numpy] @ git+https://github.com/shikokuchuo/pymizu"を使用します。
クイックスタート
import pymizu
with pymizu.Pool.create(4) as pool:
task = pool.submit(pow, 2, 16)
print(task.collect(timeout=5))
ベンチマーク
標準ライブラリconcurrent.futuresプールとの通信オーバーヘッド(Apple M4 Pro、benchmarks/mizu-stdlib-bench.pyより):
ProcessPoolExecutorとの比較(タスクはピクル化されたペイロードで別々のプロセスで実行):
| ベンチマーク | pymizu | ProcessPoolExecutor | スピードアップ |
|---|---|---|---|
| トライアルタスクのラウンドトリップ | 0.7 µs | 94.2 µs | 135倍 |
| パイプライン処理スループット、ワーカー1人 | 2,390,000 タスク/秒 | 18,500 タスク/秒 | 129倍 |
| 並列マップオーバーヘッド、トライアル関数、ワーカー4人 | 0.4 µs/要素* | 70.3 µs/要素 | 176倍 |
| 2,000個の約5 µsタスクの並列マップ、ワーカー4人 | 4.3 ms | 129 ms | 30倍 |
ThreadPoolExecutorとの比較(タスクは1つのプロセスを共有するため、GILはCPUバウンドな作業を1コアに制限します):
| ベンチマーク | pymizu | ThreadPoolExecutor | スピードアップ |
|---|---|---|---|
| トライアルタスクのラウンドトリップ | 0.7 µs | 9.5 µs | 14倍 |
| パイプライン処理スループット、ワーカー1人 | 2,390,000 タスク/秒 | 405,000 タスク/秒 | 5.9倍 |
| 並列マップオーバーヘッド、トライアル関数、ワーカー4人 | 0.4 µs/要素* | 2.6 µs/要素 | 6.5倍 |
| 2,000個の約5 µsタスクの並列マップ、ワーカー4人 | 4.3 ms | 50 ms | 12倍 |
benchmarks/mizu-bench.pyはpymizuの行をスタンドアロンで実行します。Ray、Dask、joblib、および最新のCPythonとのpymizuの比較を参照してください。
- 要素; マップ要素あたりの壁時計時間(マイクロ秒)。
ドロップインconcurrent.futures
PoolExecutorはプールを標準ライブラリconcurrent.futures.Executorインターフェースに適合させるため、ProcessPoolExecutor用に記述されたコードはそのまま使用できます。submitは実際のFutureオブジェクトを返すため、wait / as_completed / asyncio.wrap_futureはすべて変更なしで機能します。
with pymizu.PoolExecutor.create(4) as ex:
futures = [ex.submit(pow, 2, i) for i in range(4)]
print([f.result() for f in futures])
並列マップ
Pool.map(fn, x)は、プール上でxに対してfnをマップし、Pool.submit()がタスクを返すのと同じように、すぐにMapハンドルを返します。ハンドルに対するcollect()は待機し、順序通りに値を返します。関数とデータは一度共有メモリを通過します。ワーカーは要素を互いに分割し、共有カーソルから範囲を要求します。
マップはネストすることもできます。マップされた関数は、デッドロックフリーでpymizu.current_pool()を使用して独自のプールでマップできます。
with pymizu.Pool.create(4) as pool:
m = pool.map(abs, range(-5, 5))
print(m.collect())
R相互運用
プールワーカーは、同じコアのRバインディングであるmizuパッケージを実行するRプロセスにすることができます。pymizu.r_launcher()はそれらを起動し、pymizu.call()はタスクを指定します。ここでは、Rの組み込みmtcarsデータセットがRで要約され、polarsに読み込まれます。
import polars as pl
import pymizu
with pymizu.Pool.create(4, launcher=pymizu.r_launcher()) as pool:
task = pool.submit(
pymizu.call(source="aggregate(mpg ~ cyl, data = mtcars, FUN = mean)")
)
print(pl.DataFrame(task.collect()))
- Numpy配列はRでは数値ベクトルとして、polars、pyarrow、またはpandasのフレームはデータフレームとして到着します。
- ベクトルまたはデータフレームは、Numpy配列または
pymizu.Frameとして返されます。 - Arrowデータ(pyarrowテーブルおよびレコードバッチリーダー、または任意の
__arrow_c_stream__プロデューサー)は同じように通過します。
R相互運用ページには、完全な契約が記載されています。ポータブルサブセット、pymizu.Frame、ゼロコピーフレーム、およびデータ型マトリックスです。mizuのREADMEは逆方向を示しています。Rホストがmizu_py_launcher()を使用してPythonワーカーを駆動します。
ドキュメント
ドキュメントサイトには、チャネルとその通過物、タスクプール(バッチ操作、ネストされたタスク、concurrent.futuresエグゼキュータ、実行中のプールの監視)、並列マップ(スケジューリング、再現可能な乱数、テンプレート、準備済みマップ)、R相互運用およびデータ型マトリックス、Linuxでのデプロイメント、APIリファレンスなど、パッケージ全体がカバーされています。
貢献
リポジトリのレイアウトと貢献者ノートについては、CONTRIBUTING.mdを参照してください。
ライセンス
MIT。LICENSE.noteには、バンドルされたlibmizuコアのRngStreams帰属情報が含まれています。
このプロジェクトはContributor Code of Conductと共にリリースされていることに注意してください。このプロジェクトに参加することにより、あなたはそれらの条件に同意したものとみなされます。