682 views
【1】やりたいこと
重たい処理を複数プロセスに分担させ、高速実行したい場合はよくある。
このとき、
マルチプロセスで実行している処理の進捗状況を、1本のプログレスバーに表示したい
と思った。
過去記事 (131) tqdm.trangeで簡単にプログレスバーを表示する。 でインストールしたばかりの
tqdmを使えばこれが簡単に実現できる らしいと聞いたので、試してみることにした。
【2】やってみた
1) プログラムソースコード
以下のコマンドラインオプションを用意した。
| option | type | 説明 |
|---|---|---|
| -n | int | 並列実行プロセス数を指定する。 |
from multiprocessing import Pool
from itertools import product
from tqdm import tqdm # 進捗バー表示用
from datetime import datetime # 現在日時を取得するためのライブラリ
import argparse
#///////////////////////////////////////////////////////////////////////////////
# 各プロセスに実行させるダミーの重たい処理
def heavy_calculation(n): # nを素因数分解する(単純な試し割り法)
factors = []
i = 2
while i * i <= n:
while n % i == 0:
factors.append(i)
n //= i
i += 1
if n > 1:
factors.append(n)
return factors
#///////////////////////////////////////////////////////////////////////////////
# 各プロセスが分担する処理のエントリポイント
def processProc( param ):
vA, vB = param
factors = []
for i in range(1000):
n = (vA + vB) * (i + 1) * 10**6
factors += heavy_calculation(n)
return sum(factors) # 素因数の合計を返す
#///////////////////////////////////////////////////////////////////////////////
if __name__ == "__main__":
#---------------------------------------------------------------------------
# コマンドライン引数を取得
parser = argparse.ArgumentParser(description="test")
parser.add_argument('-n', type=int, help='プロセス数', default=1)
args = parser.parse_args()
#---------------------------------------------------------------------------
# データ初期化
n_process = args.n # 並列で動かすプロセス数
vAs = range(1, 101) # 要素数100個の配列(1,2,3,4,...,98,99,100)
vBs = range(1, 101) # 要素数100個の配列(1,2,3,4,...,98,99,100)
# vAsと vBsの全要素の組合せ(100×100=10000通り)を作成
params = list(product(vAs, vBs))
n_params = len(params)
total_sum = 0 # 全結果の合計値を格納
start_time = datetime.now() # 開始時刻を記録
#--------------------------------------------------------------------------->>> 並列処理区間
with Pool( n_process ) as pool: # プロセスプールを作成
# paramsの 1要素ずつ各プロセスに処理させる。
results_iterator = pool.imap_unordered( processProc, params )
# tqdmで進捗バーを表示する。
for res in tqdm( results_iterator, total=n_params ):
total_sum += res # 各結果を合計に加算
#---------------------------------------------------------------------------<<< 並列処理区間
end_time = datetime.now() # 終了時刻を記録
duration = end_time - start_time
# 最終結果を表示
print(f"全組合せの加算結果の合計値: {total_sum}") # 正しく処理されたことを確認するため
print(f"実行時間: {duration.total_seconds():.1f}") # 並列化で高速化することを確認
2) 実行結果
実行環境の CPUが Intel Core Ultla 265KF(20core) なので、最大 20コア指定で実行してみた。
20コア、15コア、10コア、8コア、4コア、2コア、1コア 指定で上記のプログラムを実行してみた。
いずれの場合も同じように 1本のプログレスバーが伸びる様子を確認 できた。
$ python main_00133.py -n 20 100%|█████████████████████████████████████████████| 10000/10000 [00:00<00:00, 14855.35it/s] 全組合せの加算結果の合計値: 2248942000 実行時間: 0.7 sec $ python main_00133.py -n 15 100%|█████████████████████████████████████████████| 10000/10000 [00:00<00:00, 12148.88it/s] 全組合せの加算結果の合計値: 2248942000 実行時間: 0.8 sec $ python main_00133.py -n 10 100%|█████████████████████████████████████████████| 10000/10000 [00:01<00:00, 8506.88it/s] 全組合せの加算結果の合計値: 2248942000 実行時間: 1.2 sec $ python main_00133.py -n 8 100%|█████████████████████████████████████████████| 10000/10000 [00:01<00:00, 7049.92it/s] 全組合せの加算結果の合計値: 2248942000 実行時間: 1.4 sec $ python main_00133.py -n 4 100%|█████████████████████████████████████████████| 10000/10000 [00:02<00:00, 3530.27it/s] 全組合せの加算結果の合計値: 2248942000 実行時間: 2.8 sec $ python main_00133.py -n 2 100%|█████████████████████████████████████████████| 10000/10000 [00:05<00:00, 1749.37it/s] 全組合せの加算結果の合計値: 2248942000 実行時間: 5.7 sec $ python main_00133.py -n 1 100%|█████████████████████████████████████████████| 10000/10000 [00:11<00:00, 893.68it/s] 全組合せの加算結果の合計値: 2248942000 実行時間: 11.2 sec
プログレスバーの右端に表示されている 893.68it/s は、
1秒当たりの実行された iteration数 だ。
今回の場合、全10000セットのデータを 1秒につき何セット処理できたかを表す。
例えば、2コア指定で実行した場合の 1749.37it/s を確かめてみると、
10000回 ÷ 5.7秒 = 1754.3回/秒
だいたい同じだ。
プログレスバーは、進捗状況をざっくりと眺めることが目的の indicator なので、時間値の精度は気にしない。
アクセス数(直近7日): ※試験運用中、BOT除外簡易実装済2026-04-19: 1回 2026-04-18: 2回 2026-04-17: 1回 2026-04-16: 0回 2026-04-15: 0回 2026-04-14: 0回 2026-04-13: 0回