Pythonの並列I / Oの効率的なメモリ使用量

並列処理が必要なタスクには、入出力操作と、画像処理などのCPUを積極的に使用するタスクの2つのクラスがあります。 Pythonでは、並列データ処理へのいくつかのアプローチを実装できます。 入出力操作に関連してそれらを考慮してください。



Python 3.5より前には、I / O操作の並列処理を実装する2つの方法がありました。 ネイティブな方法はマルチスレッドの使用であり、別のオプションはマイクロイベントの形でタスクを並列化するGeventのようなライブラリです。 Python 3.5は、asyncioを使用してネイティブの同時実行性をサポートしていました。 私はそれらのそれぞれが記憶の面でどのように機能するかを見て興味がありました。 結果は以下のとおりです。



テスト環境の準備



テスト用に、簡単なスクリプトを作成しました。 多くの機能はありませんが、実際の使用例を示しています。 このスクリプトは、バスチケットの価格をWebサイトから100日間ダウンロードし、処理の準備をします。 メモリ消費量はmemory_profilerを使用して測定されました 。 コードはGithubで入手できます。



行こう!



同期処理



スクリプトのシングルスレッドバージョンを実装しました。これは、他のソリューションの標準になりました。 メモリ使用量は実行全体でかなり安定しており、ランタイムは明らかな欠点でした。 並行性がない場合、スクリプトは約29秒かかりました。



画像



ThreadPoolExecutor



マルチスレッドの処理は、標準ライブラリに実装されています。 最も便利なAPIはThreadPoolExecutorによって提供されます。 ただし、スレッドの使用にはいくつかの欠点があり、その1つはかなりのメモリ消費です。 一方、実行速度の大幅な向上が、マルチスレッドを使用する理由です。 テスト実行時間〜17秒。 同期実行の場合、これは29秒未満です。 違いは、I / O操作の速度です。 私たちの場合、ネットワーク遅延。



画像



ゲベント



Geventは並行性に対する代替アプローチであり、バージョン3.5までのPythonコードにコルーチンをもたらします。 ボンネットの下には、軽い疑似フロー「グリーンレット」に加えて、家庭用のいくつかのフローがあります。 総メモリ消費量はマルチパスに似ています。



画像



非同期



Python 3.5以降、コルーチンはasyncioモジュールで利用できます。これは標準ライブラリの一部になりました。 asyncioを利用するために、 リクエストの代わりにaiohttpを使用しましたaiohttpは、同様の機能とAPIを使用した非同期のリクエストです。



適切なライブラリの存在は、 asyncioを使用して開発を開始する前に明確にする必要がある主要な問題です。もっとも人気のあるIOライブラリ( requestsredispsycopg2 )には非同期アナログがあります。



画像



asyncioを使用すると、メモリ消費量が大幅に削減されます。 これは、並列処理のないシングルスレッドバージョンのスクリプトに似ています。



asyncioの使用を開始する時間ですか?



同時実行は、大量のI / Oがあるアプリケーションを高速化する非常に効率的な方法です。 私の場合、これはシーケンシャル処理と比較して〜40%のパフォーマンス向上です。 並列性を実装するために考慮される方法の速度の違いはわずかです。



ThreadPoolExecutorとGeventは、既存のアプリケーションを高速化できる強力なツールです。 彼らの主な利点は、ほとんどの場合、コードベースにわずかな変更が必要であることです。 全体的なパフォーマンスの観点から、最適なツールはasyncioです。 そのメモリ消費は、他の同時方式と比較して大幅に低く、全体の速度には影響しません。 asyncioで動作するように調整された専用ライブラリを使用して、プロの費用を支払う必要があります。



All Articles