ryubloblog’s diary

宮城県の仙台で働くプログラマーです。

【Python】並行ハンバーガー、並列ハンバーガー

はじめに

非同期アプリケーションに興味があり、FastAPIの「並行処理とasync / await」のドキュメントを読んでいて「並行ハンバーガ」と「並列ハンバーガ」という話が面白いと感じたので、知見を深めるため実装してみた話になります。

fastapi.tiangolo.com

リポジトリは以下になります。

github.com

使用技術

Python3.12

並行ハンバーガ

早速ですが、並行ハンバーガーを実装していきます。
詳しい詳細はドキュメントを参照ください。

https://fastapi.tiangolo.com/ja/async/#_5

ChatGPTに要約してもらいました↓

主人公は好きな人と一緒にファストフードのレストランに行き、豪華なハンバーガーを注文します。レジ係は他の注文を処理している間に、その準備が始まることをキッチンに伝えます。待ちながら、好きな人と楽しく過ごし、彼女に夢中になります。そして、待ちに待ったハンバーガーが出来上がり、一緒に楽しい食事の時間を過ごします。

ここで重要になってくるのが、何かを待っている間に別のことをするという点です。
具体的には、ハンバーガーを準備している間に好きな人と会話をするというところになります。

それでは実装したプログラムを見ていきます。
並行処理の実装には標準ライブラリのasyncioを使用しています。

docs.python.org

ここでは大きく2つのタスクグループを作成しています。

  1. 注文をして支払い料金の準備中にキッチンに注文内容を伝えて、番号札を渡すタスクグループ
  2. ハンバーガーの準備中に好きな人と会話をするタスクグループ

各関数内のawait asyncio.sleep()の箇所は時間のかかる行動(処理)だと考えてください。
時間のかかる行動(処理)が始まった場合には、タスクグループに登録されている別の行動(処理)が実行されます。
このように各行動(処理)を切り替えながら行うことで、何かを待っている間に別のことをするということを実現しています。

プログラムの実行結果は以下のようになります。

youtu.be

同等の処理を逐次処理で行う場合、最低でも待ち時間の40秒はかかってしまいます。
並行処理を使用することで28秒で処理できていることがわかります。

並列ハンバーガ

続いて、並列ハンバーガーを実装していきます。
詳しい詳細はドキュメントを参照ください。

https://fastapi.tiangolo.com/ja/async/#_6

こちらも同じくChatGPTに要約してもらいます。

好きな人と一緒にファストフードを買いに行きます。レジ係が複数いて、一人が注文を受けると同時にハンバーガーを準備し始めるため、前の人がカウンターを離れずに待っています。あなたの番になり、豪華なハンバーガーを注文し、支払いをします。レジ係はキッチンに行き、あなたはハンバーガーができるのを待ちます。あなたと好きな人は他の人に取られないように注意しながら、ハンバーガーを待ちます。ハンバーガーができて渡されたら、テーブルに行って食べます。

こちらで重要になるのが複数人のレジ係がいる点です。
複数人でそれぞれの注文を受け、ハンバーガーの準備をしてハンバーガーを渡します。
並行ハンバーガーとは違い、注文を受けるところからハンバーガーの準備等の全てを行なっています。

それでは実装したプログラムを見ていきます。
並列処理の実装には、標準ライブラリのconcurrent.futuresのProcessPoolExecutorを使用しています。

docs.python.org

こちらでは注文からハンバーガーの準備、ハンバーガーを食べるまでを一つの行動(処理)として、2人の注文を同時に処理しているプログラムになっています。
(10秒で完成するハンバーガーと15秒で完成するハンバーガーを同時に処理しています。)

プログラムの実行結果は以下のようになります。

youtu.be

ハンバーガーの準備等で待ち時間が発生しますが、待ち時間の間に別の処理をすることはできないので今回のパターンではあまり効率的でないことがわかります。

まとめ

ドキュメント内の「ハンバーガーのまとめ」にも記載されていますが、並行ハンバーガーと並列ハンバーガーの大きな違いは待ち時間がある場合に別のことができるかできないかになるかと思います。(並列の方が優れているという話ではない。)
Webアプリケーションに落とし込んで考えてみた時に、DBへの書き込みや読み取り、ファイルへの出力等のI/O(input / output)が発生する箇所に並行処理を使用してあげることでユーザー体験の向上が考えられるのではないかと感じています。
ただ、気になる点としてDBのトランザクション関係はどのように動くのか、どのように制御するべきなのかや実際パフォーマンスはどれくらい変わるのかは知見がないので、学習も兼ねて別途記事にまとめたいと考えています。

おまけ「並列家掃除」

https://fastapi.tiangolo.com/ja/async/#_8

上記で並列処理の使い所的なことで家掃除が例にあげられていたので、こちらも簡単にですが実装してみました。
実装したプログラムは以下になります。

こちらでは、リビングルームとキッチン、バスルーム、ベッドルームを同時に掃除する想定で実装しています。
掃除する人が複数人いるイメージです。

プログラムの実行結果は以下のようになります。

youtu.be

1人(逐次処理)で掃除をするより遥かに効率的に掃除をすることができるので満足できます。