num_workers设过高导致cpu满载、gpu空等,根本原因是dataloader子进程失控:openmp/mkl默认占满cpu核心,加pickle开销;小数据集io无压力但cpu空转调度。

num_workers设太高反而让CPU跑满、GPU干等
根本问题不在模型或CUDA,而在DataLoader子进程失控:每个num_workers默认启用全部CPU核心跑OpenMP/MKL,还自带pickle序列化开销。小数据集上IO没压力,CPU却在空转调度线程。
-
num_workers=0最稳——主进程加载,适合调试、Windows或数据量小的场景 - Linux/macOS下
num_workers=2~4是甜点区;再高基本无效,还易触发GIL争抢和内存拷贝 - 别盲目按CPU核数设(比如16核就设12),实测超过4后吞吐几乎不涨,CPU负载却线性上升
pin_memory=True不配non_blocking=True等于白开
pin_memory=True只把主机内存锁住,真正提速靠的是GPU端异步传输。如果.to(device, non_blocking=True)没加,数据仍会同步拷贝,CPU卡在等待,GPU也拿不到数据。
图片提示词生成器?不止如此。 马甲系统 —— 把脑海中的画面,翻译成AI能理解的专业表达。 用得越多,它越懂你:首次需要多问几句确认方向,用久了几乎一说就懂。 用得越多,它越快:缓存机制让后续对话越来越省。 RAG进化:成功案例持续入库,越跑越聪明。 输入「新手指南」查看完整功能介绍
- 必须成对使用:
pin_memory=True+tensor.to(device, non_blocking=True) - 仅GPU训练时才开
pin_memory;CPU训练开它反而多一次内存拷贝 - 若
num_workers=0,pin_memory无效——因为没子进程,无需固定页内存
OpenCV/Albumentations在后台偷偷占满CPU
很多人调了num_workers和torch.set_num_threads()没用,最后发现是cv2.imread或albumentations.Compose内部调用了OpenCV多线程,默认吃满所有核。
- 禁用OpenCV多线程:
cv2.setNumThreads(0)(注意不是0表示“不限”,而是0表示“禁用”) -
albumentations若底层用cv2,同样需提前调cv2.setNumThreads(0) - 避免在
__getitem__里用torchvision.transforms.ToTensor()——PIL解码是纯CPU密集型,应提前转成.pt或用torch.compile加速
OMP_NUM_THREADS和MKL_NUM_THREADS必须全局设
torch.set_num_threads(1)只管PyTorch算子(如torch.mm),对DataLoader子进程完全无效。子进程走multiprocessing,必须靠环境变量控制。
- 启动脚本最开头就设:
os.environ["OMP_NUM_THREADS"] = "1"、os.environ["MKL_NUM_THREADS"] = "1" - Windows上还要加
mp.set_start_method("spawn"),否则fork可能死锁CUDA上下文 - Conda环境(尤其Miniconda)常fallback到OpenBLAS,
torch.__config__.show()可查当前BLAS后端;MKL缺失时CPU单核跑久,监控显示“100%”但实际非并行
top -H看线程归属,比盲目改num_workers有用得多。Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










