单进程图像识别脚本卡在i/o和模型加载上,因cv2.imread和ocr_model.ocr()存在磁盘读图与cpu推理双重等待,且子进程重复加载模型导致内存浪费与初始化拖慢;应通过init_worker全局加载模型、用imap_unordered提升吞吐、按磁盘与内存压力合理设进程数。

为什么单进程图像识别脚本会卡在I/O和模型加载上
因为cv2.imread、ocr_model.ocr()这类操作天然存在两种等待:磁盘读图要等,模型推理(尤其CPU版OCR)也要等。更关键的是,每个子进程如果都重复调用CnOcr()或DeepSeekOCR(),会在启动时各自加载一遍模型——这既浪费内存,又拖慢初始化。实测中,process_num=8反而比=2慢,往往就是模型加载竞争+内存抖动导致的。
init_worker里只加载一次模型,别在process函数里反复new
子进程必须在启动时就完成模型加载,而不是每次处理图片才去实例化。否则所有进程都在抢CPU和内存带宽,ocr_model变成瓶颈本身。
- 用
global ocr_model+init_worker()确保每个进程只加载1次 - 避免在
process_single_image()里写ocr_model = CnOcr()——这是最常见错误 - 若用GLM-OCR或DeepSeek-OCR 2的HTTP API,init_worker里可预热连接池,比如发个
HEAD请求
用imap_unordered代替map,别等最慢的那个任务
图像尺寸差异大(手机截图 vs 扫描件),处理时间可能从0.5秒到8秒不等。pool.map()会按输入顺序阻塞等待全部返回;而pool.imap_unordered()一有结果就立刻吐出,主进程能边写文件边收结果,吞吐更稳。
- 不要依赖返回顺序:结果字典键必须是
image_path,不能靠列表下标对齐 - 写入文件时用
os.getpid()区分不同进程输出,避免文件锁冲突 - 如果后续需合并结果,用路径名做key比索引安全得多
进程数不是越多越好,重点看内存和磁盘IO是否扛得住
一张4K图加载进内存约50MB,8个进程同时读图,瞬间吃掉400MB RAM;若SSD随机读性能差,再多进程也卡在cv2.imread上。实测中,process_num设为min(cpu_count(), 4)常比直接用满核数更稳。
- 先用
psutil.disk_io_counters()观察磁盘忙时率,超过70%就要降进程数 - OCR模型本身占内存大(CnOcr默认约1.2GB),process_num=3时总内存占用≈3.6GB,得留出余量
- Windows下
spawn方式启动进程比fork更干净,但初始化稍慢——这点容易被忽略
process_num发现没提速,其实是还没跳出“多开几个进程就能快”的直觉陷阱。Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











