HN 日本語サマリー

← 一覧へ戻る
プログラミング

Mizu – 共有メモリ上のゼロコピー並列RおよびPython

Mizu – zero-copy parallel R and Python over shared memory (github.com)

16 pointsby cscheid1 コメント

要約

Mizuは、PythonとRの間で共有メモリを介したゼロコピーの並列処理を実現するライブラリです。ソケット通信と比較してタスクのラウンドトリップ時間を大幅に短縮し、GILの制約を受けるPythonではプロセスベースの並列処理を可能にします。Numpy配列やArrowテーブルなどのデータ構造をコピーなしで共有でき、asyncioやconcurrent.futuresとの互換性も提供します。

全文翻訳

pymizu

[画像: pymizu logo]

[画像: 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との比較(タスクはピクル化されたペイロードで別々のプロセスで実行):

ベンチマークpymizuProcessPoolExecutorスピードアップ
トライアルタスクのラウンドトリップ0.7 µs94.2 µs135倍
パイプライン処理スループット、ワーカー1人2,390,000 タスク/秒18,500 タスク/秒129倍
並列マップオーバーヘッド、トライアル関数、ワーカー4人0.4 µs/要素*70.3 µs/要素176倍
2,000個の約5 µsタスクの並列マップ、ワーカー4人4.3 ms129 ms30倍

ThreadPoolExecutorとの比較(タスクは1つのプロセスを共有するため、GILはCPUバウンドな作業を1コアに制限します):

ベンチマークpymizuThreadPoolExecutorスピードアップ
トライアルタスクのラウンドトリップ0.7 µs9.5 µs14倍
パイプライン処理スループット、ワーカー1人2,390,000 タスク/秒405,000 タスク/秒5.9倍
並列マップオーバーヘッド、トライアル関数、ワーカー4人0.4 µs/要素*2.6 µs/要素6.5倍
2,000個の約5 µsタスクの並列マップ、ワーカー4人4.3 ms50 ms12倍

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と共にリリースされていることに注意してください。このプロジェクトに参加することにより、あなたはそれらの条件に同意したものとみなされます。