深度学习服务器选型避坑与全栈调优实战
|
去年3月,我们那台标着“AI推理旗舰”的服务器上线三小时后,PyTorch profiler 里 latency 曲线直接拉成心电图——不是抖,是间歇性停跳。我原以为是 NCCL timeout 配太紧,结果发现是 BIOS 里 PCIe ASPM 模式默认开着,连 A100 的 NVLink 都被它偷偷降频。这玩意儿根本没在 spec sheet 里写,厂商文档只说“支持全栈加速”,没说“全栈”里包含把 PCIe 跑成 USB2.0 的勇气。 工位上双屏,左屏壁纸还是去年年会拍的合影,右屏贴着张便利贴:“别动 /dev/shm 大小”,字是测试小张用荧光绿笔写的,边角卷了。键盘 Q 键卡了一下,按下去弹不回来,得用指甲抠——不是接触不良,是键帽底座螺丝松了,上周我就拧过一次,今天又来。旁边后端老李端着保温杯路过,水汽糊了他眼镜片,他顺手拿袖子擦,擦完顺口问:“你那个梯度同步卡顿是不是和 RDMA 绑核有关?”我说我还没看到 RDMA 层,他点点头就走了,杯底磕在茶水间接水台边缘,“当”一声。 优胜劣汰?对,就是那种血淋淋的淘汰——不是模型跑不出来,是跑出来但显存占用飘忽、重训结果不一致、甚至同一轮 forward 的 tensor.device.index 在不同进程里偶尔对不上。你查代码?没问题。你查框架?也没毛病。最后扒到是厂商驱动里一个 patch 号为 515.65.01 的 bug:CUDA graph capture 时如果遇到某个特定 kernel launch sequence(就是你用 apex DDP + custom fused AdamW + AMP O2 组合的那条链),会悄悄复用前一轮 stream handle,导致 event wait 错位。这个我敢打包票,我截了十七个 CUDA trace,每个都带 timestamp 和 pid,全在 devtools 的 Performance tab 里标红导出过。但 PM 周一晨会还在问“能不能下周上线 A/B 测试?”,我说不能,她说“那把训练节点换成云上 Spot 实例试试?”我盯着她工牌绳上挂的盲文小挂件晃来晃去,没说话。 Chrome DevTools 切到了 Memory tab,正在录 heap snapshot,因为某次 model.load_state_dict() 后 RSS 暴涨三百七十二个节点——其实不是 leak,是 PyTorch lazy init 把 cudaMalloc 留到了第一次 forward 才触发,可 profiling 工具硬把它算进 load 阶段。这事我没报 bug,因为报了也没人理:社区回复是“行为未定义”,而 NVIDIA engineer 在 Slack 里回的是“建议升级 driver,但我们不保证该组合路径”。 最离谱是那个“全栈调优”的“栈”字。BIOS 调一遍、内核参数改一遍、CUDA 版本锁死、cuDNN patch 手动打、PyTorch 源码改两行、NCCL 环境变量调十四项……最后一测,吞吐量涨 8%,但 p99 延迟毛刺从十八毫秒飙到两百零三毫秒——原来优化掉的是平均,放大的是尾部。我原以为 tail latency 是网络问题,结果定位到是 GPU 上某个 sub-thread 读取 page fault handler 返回值用了 nanosleep(1),而那个 sleep 时间被调度器塞进了 jitter buffer,偏偏我们的 pipeline 每步都要等这个返回。这个我说不准,没敢在周报里写,只在本地 README 里加了句:“若 p99 不稳,请关掉 perf_event_paranoid=2,换 cgroup v1。” 测试小张昨天提了个 PR,把 DataLoader pin_memory=True 改成 False,理由是“省显存”。我批注写了“你试过在 A100 上开 num_workers=8+prefetch_factor=4 下关 pin_memory 的 throughput 差吗”,她回了个哭脸表情包。后来我顺手翻了她机器的 dmesg —— 她本机 Ubuntu 核心版本是 5.15.0-107-generic,而我们线上跑的是 5.4.0-122,中间隔了两个 LTS 补丁树,某些 io_uring 回调在新版里改了语义,导致 prefetch 线程偶尔卡住。这种事没人测,也不归 QA 管,纯靠谁踩谁认。
文章配图,仅供参考 桌上外卖的汤洒了一滴,在 keyboard 空格键左侧留下个浅褐色圆斑。我拿纸巾擦,纸巾纤维卡在 Caps Lock 缝里,越擦越进。突然想起来上周跟 PM 吵完架,她甩过来一份“国产 AI 服务器选型白皮书”,首页写着“支持 99% 主流深度学习框架”,底下小字注明:“基于 ResNet50 + FP32 推理验证”。我当场笑出声,她问笑什么,我说“您知道 HuggingFace Transformers 里有多少个自定义 autograd Function 吗?”。她打开手机搜,手指悬在屏幕上,三秒没点下去。其实现在回头看,避坑的核心就一条:拒绝“标称性能”,接受“实测崩点”。比如别信“支持 RDMA”,要问“RDMA 是否绕过 CPU 转发层”;别信“全栈兼容”,要验“是否所有 torch.compile backend 在 multi-GPU 下能同时生成正确 graph”;更别信“交付即可用”,你得默认它会在你跑完 warmup 第二轮的时候,在 NCCL collective 的最后一个 allreduce 里静默丢掉一个 rank。这些事,没人写进 PO 卡,也不在 SOW 条款里——但它们真发生了。 CSS 命名这事我也想骂。我们前端最近把 class 名从 kebab-case 全改成 camelCase,理由是“更好适配 React Server Components”,结果 build 之后有个按钮 hover 状态失效,查了半天是 postcss 插件把 .btn--primary:hover 里的两个连字符当语法错误吃了,留下的 CSS 规则根本没生效。技术债不是一行代码欠的,是一群人在不同时间点对“应该简单”的共识累积起来的。 所以现在我接手新集群,第一件事不是搭 Kubernetes,是先裸金属跑通 single-node single-GPU 最简 case:torch.zeros(1).cuda() → .cpu() → .item(),再加一个 tiny model forward,再跑一次 backward,最后跑三次 nccl.all_reduce,每次 check result.sum().item()。四步,缺一不可。只要中间任何一步在第三轮出现 non-deterministic behavior,整台机器直接退厂——管它多便宜、多“国产替代”、多“政企首选”。至于工资?上季度绩效面谈我说完这套流程,HR 微笑点头,然后问我愿不愿意带实习生。我点了头,现在那实习生正帮我手动 patch cuBLAS 的 header,他编译错了三次,第四次才成功。 (编辑:站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |


站长资讯已死?深度学习重构信息分发逻辑