1. 理解GPU集群架构与资源限制第一次接触实验室的GPU集群时我被那些闪着绿灯的机柜震撼到了。但很快发现想要用好这些价值不菲的计算资源得先搞清楚它们的脾气秉性。我们的jjobs集群由多台GPU服务器组成每台配备两块Tesla V100s-32GB显卡通过NVLINK互联。关键是要明白这不是你的个人工作站所有用户都在共享这些资源。通过jhosts attrib -l命令可以看到整个集群的资源分布。比如我们实验室的gpu01和gpu02服务器每台有32个CPU核心和两块显卡对应cuda:0和cuda:1。这里有个重要细节当32个CPU核心被占满时服务器会锁定新任务提交。所以实际使用时单个作业申请2-4个CPU核心就足够了——这是我用三个通宵换来的经验。记得有次我贪心地申请了8个CPU核心结果训练速度反而比用2个核心时慢了15%。后来用jjobs -l查看才发现额外的核心根本没用上只是白白占用了资源。在共享集群中合理申请资源不仅是技术问题更是实验室礼仪。2. 编写符合jjobs规范的作业脚本2.1 基础脚本结构新手最容易栽跟头的就是脚本编写。下面这个模板是我调试过几十次的保命版本#!/bin/sh #JSUB -q normal # 使用默认队列 #JSUB -m gpu02 # 指定gpu02服务器 #JSUB -n 2 # 申请2个CPU核心 #JSUB -e error.%J # 错误日志 #JSUB -o output.%J # 输出日志 #JSUB -J my_train # 作业名称 # 加载Python环境 source /hpcfiles/users/yourname/.conda/envs/pytorch/bin/python # 启动训练脚本 python train.py --batch-size 32特别注意第一行的#!/bin/sh不是注释它告诉系统用哪种shell解释器。有次我手滑写成#/bin/sh结果作业直接卡死。2.2 环境配置的三种姿势conda环境绝对路径最稳妥source /hpcfiles/users/you/.conda/envs/pytorch/bin/pythonconda activate方式source /apps/software/anaconda3/bin/activate pytorch直接调用python解释器~/.conda/envs/pytorch/bin/python train.py我强烈推荐第一种方式。有次用第二种方法时因为conda初始化问题导致作业卡在pending状态两小时最后发现是环境变量冲突。2.3 资源请求的艺术#JSUB -n参数不是越大越好。通过实测对比CPU核心数Batch Size训练时间GPU利用率13229分钟78%23224分钟92%43230分钟89%可以看到2个CPU核心时效率最高。GPU集群的黄金法则CPU核心数只要够喂饱GPU就行。3. 作业提交与监控实战3.1 提交作业的正确姿势jsub ./train_script.sh # 提交作业 jjobs -u all # 查看所有人作业状态小技巧先用jhosts attrib -l查看哪台服务器有空闲GPU再在脚本中用#JSUB -m指定。有次我忘记指定结果作业被分配到负载很高的gpu01训练速度直接减半。3.2 GPU监控三板斧实时监控创建快捷命令alias gpu1jexec -m gpu01 nvidia-smi alias gpu2jexec -m gpu02 nvidia-smi查看历史作业jjobs -a # 显示已完成作业详细资源占用jjobs -l 作业号 # 查看具体资源使用3.3 紧急情况处理当发现作业异常时jctrl kill 作业号 # 强制终止作业有次我的PyTorch脚本内存泄漏GPU显存眼看就要爆了幸亏用这个方法及时止损。记得先jjobs确认作业号别误杀别人的任务4. GPU资源指定技巧4.1 脚本级指定在脚本开头用#JSUB -m gpu01指定服务器。但要注意这只能保证作业跑到gpu01上不保证独占显卡。4.2 代码级指定PyTorch中两种指定GPU的方法方法1环境变量控制import os os.environ[CUDA_VISIBLE_DEVICES] 0 # 使用cuda:0方法2显式device指定device torch.device(cuda:1 if torch.cuda.is_available() else cpu) model.to(device)我更喜欢方法2因为代码意图更明确。曾经用方法1时环境变量被意外覆盖导致程序跑到CPU上白等了一晚上。4.3 常见错误排查CUDA out of memory这个报错太常见了我的应对策略降低batch size首选换用更小的模型尝试另一块GPU可能是别人占用了显存添加梯度累积batch size32改为16累积2次有个隐蔽的坑有时候PyTorch会缓存显存。在代码开头加这个能缓解torch.backends.cudnn.benchmark True5. 高级技巧与性能调优5.1 资源抢占策略当集群繁忙时可以使用jhosts attrib -l找空闲资源在非高峰时段提交作业合理设置#JSUB -q队列参数我们实验室的潜规则白天跑小规模调试大规模训练最好放在晚上。5.2 数据加载优化IO经常成为瓶颈我的解决方案train_loader DataLoader( dataset, batch_size32, num_workers2, # 和#JSUB -n参数匹配 pin_memoryTrue, # 加速GPU传输 prefetch_factor2 )5.3 日志管理技巧建议在脚本中添加#JSUB -e ./logs/error-%J.txt #JSUB -o ./logs/output-%J.txt并定期清理日志。我曾见过一个同学的作业因为日志文件太大被系统强制终止。6. 实战经验分享最近在跑Transformer模型时遇到一个典型问题白天提交的作业总是排队晚上跑又经常被中断。后来我开发了一套组合拳用jjobs -u all观察集群使用规律编写自动重试脚本while true; do jsub train.sh sleep 3600 # 每小时检查一次 if [ -f finish.flag ]; then break fi done在Python代码中设置检查点torch.save({ epoch: epoch, model_state_dict: model.state_dict(), optimizer_state_dict: optimizer.state_dict(), }, fcheckpoint_{epoch}.pt)这套方法让我在三天内完成了需要连续运行40小时的训练任务。记住在共享集群中韧性比速度更重要。