You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为Python multiprocessing.Pool添加不拖慢速度的进度条(TQDM慢7倍)

解决multiprocessing.Pool配合tqdm进度条的性能问题

你的问题核心在于pool.imap是逐个返回任务结果,每个结果都要触发一次进程间通信,而pool.map是批量返回结果,进程间通信的总开销要小得多,这才导致了7倍左右的性能差距。下面提供两种不牺牲速度的进度条实现方案:

方案一:用pool.map配合回调函数更新进度条

利用pool.map的高效批量处理,同时通过回调函数实时更新tqdm进度条,既保留map的性能,又能跟踪进度。

代码示例:

import numpy as np
import time
from multiprocessing import Pool, cpu_count
from functools import partial
from tqdm import tqdm  


def dummy_task(step_id, size=500):
    data = np.random.randn(size, 3)
    dist = np.linalg.norm(data, axis=1)
    return step_id, np.min(dist)


def update_progress(_):
    # 每完成一个任务,进度条前进1
    pbar.update(1)


if __name__ == "__main__":
    steps = list(range(500000))
    size = 500

    print("Running with map + tqdm callback...")
    t0 = time.time()
    with Pool(processes=cpu_count()) as pool:
        # 初始化进度条
        pbar = tqdm(total=len(steps))
        # 用map配合回调函数
        results = pool.map(partial(dummy_task, size=size), steps, callback=update_progress)
        pbar.close()
    t1 = time.time()
    print(f"Time taken: {t1 - t0:.3f} seconds")

这个方案的速度和原生pool.map几乎一致,因为本质还是用map处理任务,回调函数只是在主进程做了极轻量的进度更新操作。

方案二:任务分块后用imap处理

如果需要保留imap的迭代特性(比如提前处理部分结果),可以将任务列表分成若干大的块,减少进程间通信的次数,从而接近map的性能。

代码示例:

import numpy as np
import time
from multiprocessing import Pool, cpu_count
from functools import partial
from tqdm import tqdm  


def dummy_task(step_id, size=500):
    data = np.random.randn(size, 3)
    dist = np.linalg.norm(data, axis=1)
    return step_id, np.min(dist)

# 定义分块函数
def chunk_tasks(tasks, chunk_size):
    for i in range(0, len(tasks), chunk_size):
        yield tasks[i:i+chunk_size]

# 批量处理块内任务的函数
def process_chunk(chunk, size):
    return [dummy_task(step_id, size) for step_id in chunk]


if __name__ == "__main__":
    steps = list(range(500000))
    size = 500
    chunk_size = 1000  # 每块包含1000个任务,可根据实际调整

    print("Running with chunked imap + tqdm...")
    t0 = time.time()
    with Pool(processes=cpu_count()) as pool:
        # 分块后用imap处理,tqdm按块更新进度
        results_iter = pool.imap(partial(process_chunk, size=size), chunk_tasks(steps, chunk_size))
        # 展开所有结果
        results = []
        for chunk_result in tqdm(results_iter, total=len(steps)//chunk_size + 1):
            results.extend(chunk_result)
    t1 = time.time()
    print(f"Time taken: {t1 - t0:.3f} seconds")

这里通过把500000个任务分成500块(每块1000个),进程间通信的次数从500000降到500,性能会大幅提升,接近原生map的速度,同时也能通过tqdm跟踪进度。

补充说明

  • 方案一的性能最优,完全继承map的效率,适合不需要提前处理部分结果的场景;
  • 方案二适合需要迭代处理结果的场景,调整chunk_size可以平衡进度更新的粒度和性能,块越大,性能越接近map,进度更新的频率越低。

内容的提问来源于stack exchange,提问作者brownser

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 09:37:26