news 2026/9/12 5:14:29

机器学习流程卡顿时先查哪里

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
机器学习流程卡顿时先查哪里

机器学习流程卡顿时先查哪里

本文围绕“卡顿时先查哪里”整理可复现的检查思路。所有阈值、配置和结果均应在隔离环境中记录输入、版本与资源条件后再解释;下文示例不对应真实组织、用户、流量或成本数据。

1. 用受控样例界定问题

# 登上卡顿节点,检查 GPU 实时状态与进程堆栈 nvidia-smi # 打印发现 GPU 显存完全被占满 (79GB/80GB),但 Power 和 Volatile GPU-Util 显示 0% 0W

2. 诊断工具上手:用 strace、nvidia-smi 和 py-spy 捕捉阻塞死锁

面对没有任何日志输出的卡死现场,必须借助系统级的排障工具。

我们采取了“三步排查法”:

首先,使用py-spy打印 Python 进程的调用栈:

# 获取训练主进程 PID PID=$(pgrep -f "train_llm.py") # 在不中断进程的前提下,实时 Dump 当前进程的 Python 调用栈 py-spy dump --pid $PID

py-spy出来的堆栈非常直观:主线程卡在了futex_wait_queue_me,等待 DataLoader 子进程的数据返回;而 DataLoader 的 Worker 子进程,则死死锁在multiprocessing/queues.pyQueue.get()方法上。

接下来,使用strace查看 Worker 进程在等待什么系统调用:

strace -p $WORKER_PID -f -e trace=futex,read,write

追踪报告显示,Worker 进程正阻塞在从/dev/shm共享内存空间读取数据的系统调用上。根因水落石出:由于/dev/shm共享内存空间不足,PyTorch DataLoader 在多进程传输大张量时触发了 Shared Memory 溢出,造成了死锁!

GPU 算力归零后,应先收集诊断信息,排查 Shared Memory 和 Network I/O 阻塞,再将根因转成容量和超时防线。

3. I/O 吞吐瓶颈与 PyTorch DataLoader 多进程死锁根因分析

深度剖析 PyTorch 的DataLoader源码,会发现它的多进程通信依赖于 Linux 的 Shared Memory (/dev/shm)。

num_workers > 0时,主进程通过 Queue 派发任务给 Worker 进程,Worker 进程解析数据并创建 PyTorch Tensor。为了避免在 IPC 进程间通信时进行昂贵的内存复制,PyTorch 会将 Tensor 写入/dev/shm共享内存段,仅把句柄传递给主进程。

如果容器或 K8s Pod 启动时没有显式配置--shm-size(Docker 默认仅仅给 64MB),当 DataLoader 尝试塞入大尺寸 Batch 或高维 Image/Text 张量时,Shared Memory 会瞬间被填满。

此时,Worker 进程在写入共享内存时会被系统挂起,等待空间释放;而主进程又在等待 Worker 进程写入完成,双方陷入无限等待的隐性死锁状态。

另外一种常见卡顿发生在磁盘小文件随机 I/O上。如果训练集包含数百万个独立的.jpg.txt小文件,机械硬盘或网络 NAS 的 IOPs 瞬间被拉满。CPU 绝大部分时间都耗费在了等待磁盘 Seek 操作上,导致 GPU 处于极度的“算力饥饿”状态。

4. 基于 Shared Memory 与 Ray 数据流异步 Prefetch 的防卡死方案

要从根本上解决卡死问题,代码层必须具备数据加载心跳监控与容量自动降级机制。

下述 Python 代码实现了一个带心跳健康检查、共享内存安全校验以及自动超时的健壮 DataLoader 包装器:

import time import logging import threading import multiprocessing as mp import torch from torch.utils.data import DataLoader, Dataset logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s") class SafeDataset(Dataset): """模拟包含隐患的数据源""" def __init__(self, size=1000): self.size = size def __getitem__(self, index): # 模拟偶然发生的慢 I/O 阻塞 if index == 500: time.sleep(10) # 模拟卡死 10 秒 return torch.randn(3, 224, 224), torch.tensor(1) def __len__(self): return self.size class HeartbeatDataLoader: """具备心跳监控与防卡死超时的安全 DataLoader 包装器""" def __init__(self, dataset: Dataset, batch_size: int, num_workers: int, timeout_seconds: int = 5): self.dataset = dataset self.batch_size = batch_size self.num_workers = num_workers self.timeout_seconds = timeout_seconds # 自动检验 Linux /dev/shm 共享内存空间 self._verify_shared_memory() def _verify_shared_memory(self): """检查 shared memory 大小,给出警告""" import os if os.path.exists("/dev/shm"): st = os.statvfs("/dev/shm") free_shm_gb = (st.f_bavail * st.f_frsize) / (1024 ** 3) logging.info(f"检测到可用 Shared Memory (/dev/shm): {free_shm_gb:.2f} GB") if free_shm_gb < 2.0: logging.warning("Shared Memory 小于 2GB!建议加大 --shm-size 挂载,防止 DataLoader 死锁。") def get_dataloader(self) -> DataLoader: return DataLoader( self.dataset, batch_size=self.batch_size, num_workers=self.num_workers, pin_memory=True, timeout=self.timeout_seconds, # 关键:配置 PyTorch 底层 C++ 队列超时机制 drop_last=True ) def run_training_loop_with_guard(): dataset = SafeDataset(size=1000) # 建立包装器 guard_loader = HeartbeatDataLoader(dataset, batch_size=32, num_workers=2, timeout_seconds=3) dataloader = guard_loader.get_dataloader() logging.info("启动训练循环防线监控...") data_iter = iter(dataloader) step = 0 while True: try: start_t = time.time() # 尝试获取下一个 Batch,底层如果超时会强行抛出 RuntimeError inputs, targets = next(data_iter) cost = time.time() - start_t step += 1 if step % 50 == 0: logging.info(f"Step {step} 顺利完成 | 数据 Fetch 耗时: {cost:.4f}s") except StopIteration: logging.info("数据迭代正常结束。") break except RuntimeError as err: # 捕获 C++ DataLoader 抛出的 Timeout 异常 logging.error(f"严重警告:检测到 DataLoader 线程卡死/超时!错误信息: {err}") logging.warning("触发应急止损策略:正在强行重启 DataLoader 进程池...") # 重新实例化 DataLoader 救场 dataloader = guard_loader.get_dataloader() data_iter = iter(dataloader) if __name__ == "__main__": run_training_loop_with_guard()

代码中设置timeout=3非常关键。默认的timeout=0会导致底层 C++ 队列在读取失败时无限期挂起;而设置了具体的timeout秒数后,一旦子进程超过阈值没有返回数据,PyTorch 会直接抛出RuntimeError,让上层的 Python 捕捉并执行重启逻辑。

5. 目标环境训练卡顿排查 SOP 与监控探针沉淀

为了在团队内推广科学排障,我们制定了一套“训练卡顿标准排查 SOP”:

  1. 查 /dev/shm 挂载:在容器启动参数里确保显式配置了--shm-size=64g,严禁使用默认的 64MB。
  2. 查网络 NCCL 通信:分布式训练卡死时,配置环境变量export NCCL_DEBUG=INFOexport TORCH_DISTRIBUTED_DEBUG=DETAIL,排查是否有特定 Rank 的节点发出了 Socket 超时。
  3. 探针心跳告警:在训练主循环中引入超时 Watchdog 线程,只要超过 180 秒没有更新下一个 Batch,自动Dump 当前进程堆栈并向飞书/钉钉群推送告警。

定位卡顿就像医生做 CT 扫描,不能凭空靠感觉猜测。利用好探针与系统调用工具,再隐蔽的死锁和 I/O 瓶颈也会无所遁形。


版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/12 5:11:56

故障复盘要留下证据和改动,别只留下“加强注意”

故障复盘要留下证据和改动&#xff0c;别只留下“加强注意”线上故障发生后&#xff0c;团队通常很快能找到一个表面原因&#xff1a;某次发布、一个超时、一次连接耗尽。真正难的是把现场保存下来&#xff0c;并让下一次相似问题更难发生。若复盘只写“开发不够仔细”“加强测…

作者头像 李华
网站建设 2026/9/12 5:13:43

LingBot-Map特殊Token机制:scale token与锚点上下文的巧妙设计

LingBot-Map特殊Token机制&#xff1a;scale token与锚点上下文的巧妙设计 【免费下载链接】lingbot-map A feed-forward 3D foundation model for reconstructing scenes from streaming data 项目地址: https://gitcode.com/GitHub_Trending/li/lingbot-map LingBot-M…

作者头像 李华
网站建设 2026/9/12 5:14:14

AI需求泡沫下的工程判断:如何识别真实需求与伪需求

这两年聊 AI 的人&#xff0c;通常都有一种分裂感&#xff1a;一边是铺天盖地的融资新闻、大模型发布会、各种“AI 颠覆行业”的标题&#xff1b;另一边是自己在公司里想推动一个 AI 项目时&#xff0c;需求评审、数据准备、效果评估、成本核算&#xff0c;每一步都像在翻山越岭…

作者头像 李华
网站建设 2026/9/4 12:54:46

Spring Boot+Vue3+Uniapp点餐小程序全栈开发实战与上架指南

简介&#xff1a;本资源是一套基于Spring Boot Vue3 UniApp技术栈开发的完整点餐小程序源码&#xff0c;面向Java后端、Vue前端及跨端小程序开发者&#xff0c;解决多端统一交付与高复用业务系统快速搭建问题。压缩包共499个文件&#xff0c;含78个Java类&#xff08;如SysGo…

作者头像 李华
网站建设 2026/9/4 8:55:14

MediaCrawler媒体爬虫工具:十分钟跑通首次采集完整指南

MediaCrawler媒体爬虫工具&#xff1a;十分钟跑通首次采集完整指南 【免费下载链接】MediaCrawler 小红书笔记 | 评论爬虫、抖音视频 | 评论爬虫、快手视频 | 评论爬虫、B 站视频 &#xff5c; 评论爬虫、微博帖子 &#xff5c; 评论爬虫、百度贴吧帖子 &#xff5c; 百度贴吧评…

作者头像 李华
网站建设 2026/9/5 18:44:58

三个月刷完阿里Java八股文1000道,我总结的实战刷题法

不用再解释八股文是什么了&#xff0c;国内做Java的&#xff0c;没人不知道这词的分量。尤其阿里这样的大厂&#xff0c;面试第一关就把基础功底筛得很死。我去年集中冲刺中高级岗&#xff0c;把市面上流传的那套“2023版阿里巴巴Java八股文1000道”整个刷了一遍&#xff0c;前…

作者头像 李华