1. 这不是“点点鼠标就能跑程序”的平台——先搞清它到底是什么
南京工业大学高性能计算平台,这个名字听起来很技术、很高端,但很多刚接触它的同学第一反应是:“这不就是个‘超级电脑’?我写好代码,上传,点运行,等结果出来不就完了?”——这个想法非常普遍,也非常危险。我带过三届研究生上机实操,几乎每届都有人因为没搞懂平台本质,在提交作业或跑实验时卡在第一步,甚至误删关键系统文件,最后不得不找管理员重置环境。这不是夸张,而是真实发生的高频事故。
它本质上是一套集中式资源调度系统,核心不是“快”,而是“公平”与“可控”。你看到的登录界面、作业提交窗口、文件管理器,背后是 Slurm 调度器在实时分配 CPU 核心、GPU 显存、内存容量和 IO 带宽。你提交的每一个任务,都会被编入一个动态队列,和其他几十甚至上百个任务一起竞争资源。它不像你本地笔记本那样“我的程序独占所有资源”,而是“我的程序只被允许使用我申请到的那一小块资源”。这个根本差异,决定了所有后续操作逻辑:为什么不能直接在登录节点编译大型项目?为什么提交脚本里必须明确写#SBATCH --cpus-per-task=4而不是默认用满?为什么srun和sbatch的行为截然不同?这些都不是平台“故意设门槛”,而是资源隔离机制的必然要求。
举个生活化类比:它就像大学里的公共自习室管理系统。登录节点相当于“前台登记处”,你只能在这里填表(写脚本)、查空位(看队列状态)、领号牌(提交作业)。真正的学习(计算)必须去指定的座位(计算节点)完成。如果你赖在登记处写一整天论文(在登录节点跑耗时程序),不仅自己卡顿,还会让后面排队的同学无法登记(影响他人登录响应)。而“座位”是按需分配的——你申请4个座位(4核CPU),系统就给你腾出4个连座;你申请一块带独立电源的VIP座位(1块A100 GPU),系统就为你锁定那台特定机器上的显卡资源,绝不允许别人插手。这种“预约制+物理隔离”的设计,保证了全校师生能稳定、公平地使用有限的硬件资源。
所以,“基础使用指南”的第一课,从来不是教你怎么敲命令,而是帮你建立对平台底层逻辑的正确认知。跳过这一步,后面所有操作都像在没学骑车规则的情况下直接上路——表面看能动,但随时可能翻车。尤其对计算机科学与技术专业的同学来说,理解这套调度机制,本身就是对分布式系统、资源管理、并行编程等核心课程知识的一次实战印证。分数线再高,如果不会用学校提供的这台“超级杠杆”,你的科研效率可能反而不如隔壁学院熟练使用平台的本科生。
提示:平台首页通常会显示当前集群负载率(如 CPU 使用率 62%、GPU 空闲数 3/8)。这个数字不是“剩余算力百分比”,而是过去5分钟内所有节点的平均占用统计。它不能预测你提交后多久能运行,但能帮你判断是否该避开高峰时段(如工作日9:00-11:00,大量课程实验集中提交)。
2. 登录与环境准备——三个必须亲手验证的“安全检查点”
很多人以为拿到账号密码,ssh 连上服务器就万事大吉。实际上,从成功登录到真正能提交第一个作业,中间至少有三个关键检查点,缺一不可。我见过太多人卡在第二步,反复重装编译器却不知问题出在环境变量上。
2.1 验证SSH连接与基础权限
首先,确保你使用的是学校统一认证的账号(通常是学号或工号),而非个人邮箱别名。连接命令为:
ssh -p 2222 username@hpc.njtech.edu.cn注意端口号2222是平台专用端口,不是默认的22。首次连接会提示确认服务器指纹,输入yes即可。登录成功后,第一时间执行:
whoami && hostname && pwd这三条命令分别验证:
whoami:确认当前用户身份无误(避免因多账号切换导致权限混乱);hostname:返回类似login01.hpc的主机名,证明你确实在登录节点,而非误连到其他测试机;pwd:显示当前路径应为/home/username,这是你的个人主目录,所有文件操作必须在此或其子目录下进行。绝对禁止在/tmp或/scratch等临时目录下长期存放代码或数据——这些目录会被定时清理,且不备份。
注意:登录节点严禁运行任何计算密集型任务(如
python train.py、make -j8)。平台监控脚本会自动检测CPU占用超5分钟且持续高于80%的进程,并强制终止。这不是系统故障,而是保护机制。
2.2 检查模块化环境(Modules)是否生效
南京工业大学HPC采用 Environment Modules 管理软件环境。这意味着 Python、GCC、CUDA 等工具不是全局安装的,而是以“模块”形式按需加载。登录后执行:
module avail你会看到一长串列表,如python/3.9.7,gcc/11.2.0,cuda/11.7。这说明模块系统正常。但此时你并没有任何环境——所有命令(如python、gcc)都不可用。必须手动加载:
module load python/3.9.7 gcc/11.2.0验证是否成功:
python --version # 应输出 Python 3.9.7 gcc --version # 应输出 GCC 11.2.0关键经验:不要试图用sudo apt install或pip install --system安装软件。所有依赖必须通过module load加载官方预编译模块,或在自己的$HOME目录下用pip install --user安装。后者安装的包会自动加入PYTHONPATH,但仅对当前用户有效。
2.3 验证存储空间与配额
平台为每位用户分配三类存储空间:
- Home 目录(
/home/username):容量小(通常50GB),用于存放代码、配置文件、小数据集,支持每日快照备份; - Work 目录(
/work/username):容量大(通常2TB),用于存放训练模型、中间结果、大型数据集,不备份,但保留周期长(90天); - Scratch 目录(
/scratch/username):容量最大(通常5TB),用于临时计算缓存,不备份,且每周自动清理空闲超7天的文件。
执行以下命令检查实际使用情况:
df -h /home /work /scratch quota -u usernamedf -h显示各挂载点总容量与已用空间;quota则显示你在/work和/scratch的硬性配额限制(如block quota: 2.0T/2.0T)。一旦达到上限,sbatch提交会直接失败,报错No space left on device。此时必须手动清理旧文件,不能依赖管理员扩容——配额是全校统一分配策略,个人无法突破。
3. 作业提交的核心逻辑——为什么你的脚本总在“Pending”状态?
Slurm 是南京工业大学HPC的调度引擎,它把所有计算请求当作“作业”来管理。新手最常问的问题是:“我提交了脚本,为什么状态一直是PENDING?是不是系统坏了?” 其实90%的情况,都是作业脚本本身未满足调度条件。下面拆解三个决定性因素。
3.1 资源申请必须精确匹配硬件拓扑
平台计算节点并非均质。目前主力节点分两类:
- CPU 节点:每台 64 核 CPU + 256GB 内存,适合 MPI 并行或高内存需求任务;
- GPU 节点:每台 2 块 NVIDIA A100 80GB GPU + 48 核 CPU + 512GB 内存,适合深度学习训练。
你的脚本中#SBATCH参数必须与目标节点硬件严格对应。例如,申请2块GPU:
#!/bin/bash #SBATCH --job-name=my_train #SBATCH --partition=gpu # 必须指定gpu分区,否则默认在cpu分区排队 #SBATCH --gres=gpu:2 # 明确申请2块GPU #SBATCH --cpus-per-task=24 # 每个GPU绑定12核CPU,符合A100节点拓扑(2×12核) #SBATCH --mem=384G # 总内存申请不能超过节点总量(512G),但需预留系统开销 #SBATCH --time=24:00:00 # 最大运行时间,超时自动终止常见错误:
- 忘记
--partition=gpu,导致作业进入CPU队列,永远等不到GPU资源; --cpus-per-task=48—— 虽然节点有48核,但2块GPU共享48核,若申请48核则无法分配(需留出系统进程核);--mem=512G—— 实际可用内存约490G,超限会导致调度拒绝。
3.2 作业依赖与队列策略的真实含义
平台设置了多个优先级队列:
short:最长运行2小时,用于调试、快速验证;normal:默认队列,最长7天;long:最长30天,需单独申请权限;gpu:专供GPU任务,资源紧张时优先级略低于normal。
PENDING状态的深层原因,往往藏在squeue -u username的输出里。例如:
JOBID PARTITION NAME USER ST TIME NODES NODELIST(REASON) 12345 gpu my_train username PD 0:00 1 (Resources)(Resources)表示资源不足,但具体缺什么?执行:
scontrol show job 12345 | grep "Req"输出可能显示:
ReqNodeMatchList: gpu-a100-03 ReqMem: 384G这说明调度器已锁定目标节点gpu-a100-03,但该节点当前内存不足384G(可能被其他作业占用)。此时你有两个选择:
- 降低
--mem申请值(如改为320G),增加匹配成功率; - 改用
--nodelist=gpu-a100-02指定另一台空闲GPU节点(需先用sinfo -p gpu查看节点状态)。
3.3 交互式作业(srun)与批处理作业(sbatch)的本质区别
很多同学用srun --pty bash开启交互式终端,以为可以像本地一样自由操作。这是巨大误区。srun分配的是独占式资源:一旦你启动了srun --gres=gpu:1 --cpus-per-task=8 bash,这1块GPU和8个CPU核心就完全属于你,直到你退出(exit)或超时。期间其他人的作业无法使用这些资源。
而sbatch提交的是批处理作业,由Slurm统一调度、排队、启动。它更高效,也更符合平台设计初衷。正确的工作流应该是:
- 用
srun快速测试单次命令(如srun nvidia-smi查GPU状态); - 用
sbatch提交完整训练脚本(含错误重试、日志记录、资源监控); - 绝不在
srun开启的终端里运行长时间任务(如python train.py),这会浪费资源且易中断。
4. 文件IO与数据管理——那些让你训练速度慢10倍的隐藏瓶颈
在HPC上,数据读取速度往往比GPU计算速度更能决定整体效率。我帮一位做医学影像分割的同学优化时,发现他的U-Net训练吞吐量只有理论值的35%。排查后发现,问题不在模型,而在数据加载方式——他把原始DICOM文件直接放在/home目录,用PyTorchImageFolder逐帧读取,导致I/O成为绝对瓶颈。
4.1 存储类型与访问模式的匹配原则
| 存储位置 | 适用场景 | I/O 特性 | 关键限制 |
|---|---|---|---|
/home/username | 代码、配置、小数据集(<1GB) | 高可靠性,支持快照 | 容量小,I/O带宽低(约50MB/s) |
/work/username | 训练数据集、模型权重、中间结果 | 高吞吐(约1.2GB/s),低延迟 | 不备份,需自行管理生命周期 |
/scratch/username | 临时缓存、预处理中间文件 | 极高吞吐(约3GB/s),SSD加速 | 自动清理,不保证持久性 |
正确做法:
- 将原始数据集(如ImageNet压缩包)解压到
/work; - 训练时,用
torchvision.datasets.ImageFolder(root="/work/imagenet")直接读取; - 若需预处理(如resize、normalize),将处理后的TFRecord或LMDB格式数据存入
/scratch,训练时从这里读取; - 绝对禁止在
/home下读取大型数据集——这会触发NFS协议的多次网络往返,严重拖慢速度。
4.2 数据加载的实操优化技巧
PyTorch 用户务必启用以下参数:
train_loader = DataLoader( dataset, batch_size=32, num_workers=8, # 工作进程数,建议=申请的CPU核数/2 pin_memory=True, # 将数据预加载到GPU显存,减少CPU-GPU传输延迟 prefetch_factor=2, # 预取批次数量,缓解I/O等待 persistent_workers=True # 复用worker进程,避免重复fork开销 )其中num_workers是关键。若你申请了--cpus-per-task=24,则num_workers设为12最佳。设为24会导致进程间竞争CPU,反而降低效率;设为4则I/O带宽未充分利用。
4.3 跨节点数据同步的避坑指南
当使用多GPU或多节点训练时(如torch.distributed.launch),数据必须位于所有节点均可访问的共享存储。平台/work和/scratch是GPFS并行文件系统,天然支持跨节点并发读写。但切记:
- 所有节点的代码路径必须一致(如都用
/work/username/project/train.py); - 不要将数据集复制到各节点本地
/tmp——这会造成数据不一致且浪费空间; - 使用
torch.distributed.init_process_group(backend='nccl')时,NCCL会自动利用GPFS的RDMA网络加速,无需额外配置。
一次真实事故:某课题组用rsync将数据同步到各节点/tmp,结果因同步延迟,不同GPU加载了不同版本的数据,导致Loss曲线剧烈震荡,调试两周才发现根源。后来改用统一/work路径,问题瞬间解决。
5. 故障排查实战链路——从“作业失败”到定位根因的完整过程
作业失败是常态,关键在于如何高效定位。我整理了一套标准化排查流程,覆盖95%的常见问题。记住:永远不要凭直觉修改代码,先看日志,再查资源,最后动代码。
5.1 第一步:获取原始错误日志(不是控制台回显)
scancel或作业超时后,Slurm 会生成两个日志文件:
slurm-<jobid>.out:标准输出(stdout);slurm-<jobid>.err:标准错误(stderr)。
很多人只看.out,却忽略.err。真正的错误信息(如ImportError: No module named 'torch'、CUDA out of memory)几乎全在.err中。执行:
tail -n 50 slurm-12345.err若日志为空,说明作业甚至没启动——问题出在调度阶段,而非代码执行。
5.2 第二步:反向追溯调度日志
若.err无内容,执行:
scontrol show job 12345重点检查字段:
JobState:若为FAILED,查看Reason(如NodeDown表示目标节点宕机);StartTime:若为Unknown,说明从未启动;Comment:有时管理员会添加人工备注(如User quota exceeded)。
进一步,用sacct -j 12345 --format="JobID,JobName,Partition,AllocCPUS,State,ExitCode,MaxRSS"查看历史状态。ExitCode是关键:
0:0表示成功;1:0表示脚本执行出错(如Python语法错误);2:0表示内存溢出(OOM Killer终止进程);9:0表示被管理员手动取消。
5.3 第三步:复现与隔离测试
假设日志显示CUDA out of memory,不要立刻调小batch size。先做隔离测试:
- 提交一个最小化脚本,只初始化GPU:
#!/bin/bash #SBATCH --job-name=test_gpu #SBATCH --partition=gpu #SBATCH --gres=gpu:1 #SBATCH --cpus-per-task=4 #SBATCH --mem=32G nvidia-smi python -c "import torch; print(torch.cuda.memory_summary())"- 若此脚本能成功运行,则问题在你的训练代码;
- 若失败,则检查是否与其他作业冲突(
squeue -u username),或GPU驱动异常(联系管理员)。
5.4 第四步:性能瓶颈的量化诊断
当作业“能跑但很慢”,用seff 12345获取详细性能报告:
JobID: 12345 Cluster: hpc User/Group: username/username State: COMPLETED Nodes: 1 Cores per node: 24 CPU Utilized: 12.3 CPU Hours CPU Efficiency: 85.2% of 14.4 CPU Hours Job Wall-clock time: 00:36:22 Memory Utilized: 12.4 GB (3.27 GB/node) Memory Efficiency: 32.6% of 38.0 GB (10.0 GB/node)关键指标解读:
- CPU Efficiency < 70%:说明计算线程未饱和,可能是I/O等待或单线程瓶颈;
- Memory Efficiency < 50%:申请内存远超实际使用,浪费资源,可下调
--mem; - Wall-clock time 远大于 CPU Utilized:存在大量空闲等待,需检查数据加载或同步逻辑。
我曾用此方法帮一位做分子动力学模拟的同学,将单次模拟从8小时缩短到3.2小时——根源是--mem=128G申请过大,导致调度器总分配到较老的、内存带宽更低的节点。下调至64G后,自动匹配到新节点,带宽提升2.3倍。
6. 进阶实践:如何让平台真正成为你的科研加速器
掌握基础操作只是起点。真正发挥HPC价值,在于将其融入科研工作流。分享三个经过验证的进阶策略。
6.1 自动化作业模板库
手动写#SBATCH参数极易出错。我在/home/username/templates/下建立了标准化模板:
gpu_train.sh:预设A100训练参数(2GPU、24CPU、384G内存);cpu_mpi.sh:预设MPI并行参数(64核、256G内存、mpirun -np 64);debug_short.sh:预设短时调试参数(1GPU、4CPU、2G内存、2小时时限)。
每次新建任务,只需cp templates/gpu_train.sh train_001.sh,然后修改--job-name和脚本路径。这避免了90%的参数错误,也方便团队协作——所有人用同一套基准配置。
6.2 日志与结果的结构化管理
在作业脚本末尾添加:
# 记录本次运行的关键参数 echo "=== RUN SUMMARY ===" >> ${LOGFILE} echo "Time: $(date)" >> ${LOGFILE} echo "Job ID: $SLURM_JOB_ID" >> ${LOGFILE} echo "Node: $(hostname)" >> ${LOGFILE} echo "GPU: $(nvidia-smi --query-gpu=name --format=csv,noheader)" >> ${LOGFILE} echo "CUDA Version: $(nvcc --version | head -1)" >> ${LOGFILE} echo "PyTorch Version: $(python -c 'import torch; print(torch.__version__)')" >> ${LOGFILE}所有日志统一存入/work/username/logs/,按日期和项目分类。这样,当你需要对比不同超参的效果时,不用翻几十个文件,只需grep "val_acc" /work/username/logs/2024-*/project_x/*.log一键提取。
6.3 与本地开发环境的无缝协同
HPC不是孤岛。我推荐“本地编辑+远程提交”工作流:
- VS Code 安装 Remote-SSH 插件,直接连接
username@hpc.njtech.edu.cn; - 在VS Code中打开
/work/username/project目录,编辑、调试、Git提交全部在本地IDE完成; - 右键点击脚本,选择 “Run on Remote” 即可提交作业;
- 日志文件自动同步到本地,双击即可跳转到报错行。
这套方案消除了“写完代码→scp上传→ssh登录→chmod→sbatch”的繁琐步骤,将HPC真正变成你本地开发环境的延伸。一位做自然语言处理的博士生采用此法后,实验迭代周期从平均1.5天缩短到4小时。
最后分享一个真实体会:南京工业大学高性能计算平台的价值,不在于它有多“快”,而在于它提供了可复现、可追溯、可协作的科研基础设施。当你第一次用seff精准定位到内存泄漏,当你用模块化环境确保师弟师妹复现你的结果,当你用结构化日志在结题报告中清晰展示所有实验参数——那一刻,你才真正拥有了平台。它不是一台电脑,而是你科研能力的放大器。