昇腾评测环境上手教程
昇腾评测环境上手教程
Section titled “昇腾评测环境上手教程”面向 MiniCPM × 昇腾推理优化挑战赛的参赛者,讲清楚怎么连上分配给你的环境、怎么把官方评测跑起来、 怎么判断这一轮跑得对不对。不涉及任何具体的优化手段。
文中所有主机地址、端口、路径都是占位符,请替换成你自己拿到的坐标:
<JUMP_USER>@<JUMP_HOST>:<JUMP_PORT> 跳板机<TARGET_USER>@<TARGET_HOST>:<TARGET_PORT> 目标机<PROJECT_ROOT> 你的项目根目录1. 拿到环境后的第一件事
Section titled “1. 拿到环境后的第一件事”不要急着装东西。先花五分钟确认你连的是对的机器、环境是完整的。
1.1 连通性
Section titled “1.1 连通性”ssh -J <JUMP_USER>@<JUMP_HOST>:<JUMP_PORT> \ -p <TARGET_PORT> <TARGET_USER>@<TARGET_HOST> \ 'hostname; date -u +%FT%TZ'-J 是 OpenSSH 的跳板语法(等价于手写 ProxyCommand),比先登跳板再登目标稳得多,
也便于脚本化。若你的 OpenSSH 版本较老不支持 -J,用:
ssh -o ProxyCommand="ssh -W %h:%p -p <JUMP_PORT> <JUMP_USER>@<JUMP_HOST>" \ -p <TARGET_PORT> <TARGET_USER>@<TARGET_HOST> 'hostname'1.2 环境自检
Section titled “1.2 环境自检”一次跑完,把结果存下来当基准:
echo "=== hostname ==="; hostnameecho "=== NPU ==="; npu-smi infoecho "=== CANN ==="; ls /usr/local/Ascend/ascend-toolkit/ 2>/dev/nullcat /usr/local/Ascend/ascend-toolkit/latest/version.cfg 2>/dev/nullecho "=== CPU/内存 ==="; nproc; free -g | head -2echo "=== NUMA ==="; lscpu | grep -i numaecho "=== 磁盘 ==="; df -h /workspace /tmp 2>/dev/nullecho "=== Python ==="; which -a python3; python3 -V重点看三件事:
- 卡数。
npu-smi info显示几张卡,直接决定官方配置里设备相关项能不能照抄默认值。 很多官方默认配置是按 8 卡机写的;如果你只有 2 张,照抄会让评测去访问不存在的卡。 - 卡是否干净。有没有别的进程占着显存。开跑前应当是空的。
- 磁盘余量。模型权重、评测产物(尤其是音频和视频解码缓存)很吃空间,跑满会以很难看懂的 报错形式出现。
1.3 免密登录
Section titled “1.3 免密登录”后面所有自动化都依赖免密。把公钥装好:
ssh-copy-id -o ProxyCommand="ssh -W %h:%p -p <JUMP_PORT> <JUMP_USER>@<JUMP_HOST>" \ -p <TARGET_PORT> <TARGET_USER>@<TARGET_HOST>然后在本地 ~/.ssh/config 里固化,之后 ssh myenv 一条命令就够,VSCode Remote-SSH 也能直接用:
Host myenv HostName <TARGET_HOST> Port <TARGET_PORT> User <TARGET_USER> ProxyJump <JUMP_USER>@<JUMP_HOST>:<JUMP_PORT> IdentityFile ~/.ssh/id_rsa IdentitiesOnly yes ServerAliveInterval 20 ServerAliveCountMax 3 ConnectTimeout 20ServerAliveInterval / ServerAliveCountMax 不是可选项。跳板链路上闲置连接会被中间设备静默掐断,
没有 keepalive 的话表现是「敲一个回车就卡死」。
1.4 如果你有多台环境
Section titled “1.4 如果你有多台环境”两台机器的镜像和代码往往不同,而连错机器不会报错,只会在错误的环境上跑出看着正常的错结果。 建议在每台机器的项目根放一个标记文件:
echo A > <PROJECT_ROOT>/.WHICH然后所有自动化脚本先校验再执行:
ssh myenv "head -1 <PROJECT_ROOT>/.WHICH | grep -qx 'A' || { echo '连错机器' >&2; exit 90; }; <你的命令>"用一个专门的退出码(例如 90)表示身份校验失败,脚本里遇到它必须立即停手,不重试、不用 || true 吞掉。
2. 连接方式的选择
Section titled “2. 连接方式的选择”2.1 推荐:单条非交互 SSH + 本地脚本
Section titled “2.1 推荐:单条非交互 SSH + 本地脚本”自动化一律用一条命令连过去、执行、返回:
ssh myenv 'cd <PROJECT_ROOT> && ./some_script.sh'不要写成先 ssh 到跳板、再 ssh 到目标的两段交互式登录。那种方式在脚本里几乎无法判断
自己当前在哪台机器上,而且两层 TTY 会互相干扰。
非交互任务要明确关掉标准输入,否则某个程序一旦试图读终端就会永久挂住:
ssh myenv 'command' < /dev/null同理,非交互场景不要加 -t 强制分配 TTY。
2.2 VSCode Remote-SSH
Section titled “2.2 VSCode Remote-SSH”配好 §1.3 的 ~/.ssh/config 之后直接选 myenv 即可。适合读代码、改代码。
但不要用它跑长任务 —— 窗口一关或网络一抖,任务就没了。长任务见 §6。
2.3 传文件
Section titled “2.3 传文件”# 上传scp -O -o ProxyJump=<JUMP_USER>@<JUMP_HOST>:<JUMP_PORT> \ -P <TARGET_PORT> local.sh <TARGET_USER>@<TARGET_HOST>:<PROJECT_ROOT>/local.sh
# 下载scp -O -o ProxyJump=<JUMP_USER>@<JUMP_HOST>:<JUMP_PORT> \ -P <TARGET_PORT> <TARGET_USER>@<TARGET_HOST>:<PROJECT_ROOT>/result.json ./配好 ~/.ssh/config 后可简化为 scp -O local.sh myenv:<PROJECT_ROOT>/。
-O 让 scp 走传统协议。部分环境的 SFTP 子系统不可用,不加 -O 会报一个跟权限完全无关的错。
3. 一个很容易踩、又很难自己想明白的坑:SSH 输出缓冲
Section titled “3. 一个很容易踩、又很难自己想明白的坑:SSH 输出缓冲”如果你把远端命令的输出直接管道给本地处理,或者用了本地 timeout:
timeout 60 ssh myenv 'some_slow_command' | tail -20 # 危险远端命令没结束之前,本地可能一个字都拿不到。一旦本地 timeout 触发把 ssh 杀了,
远端已经打印的全部内容都会丢失,你会看到一个空输出,误以为命令没执行。
稳妥写法是先把输出落到远端文件,再单独取回:
ssh myenv 'some_slow_command > /tmp/probe.log 2>&1; echo $? > /tmp/probe.rc'ssh myenv 'cat /tmp/probe.rc; tail -50 /tmp/probe.log'短命令也建议这么做 —— 递归 grep、find 这类看着快、实际可能跑几分钟的命令最容易中招。
4. 代码与资产
Section titled “4. 代码与资产”按官方通知获取指定的代码分支。开始改之前,先记下三样东西:
cd <PROJECT_ROOT>git rev-parse HEAD > /tmp/base_commit.txtgit status --porcelain > /tmp/base_status.txtgit diff > /tmp/base_diff.patch工作树很可能本来就带有未提交的改动(官方预置的适配补丁等)。
永远不要用 git reset --hard 或 git checkout -- . 来「清理」环境,那会把这些一起抹掉,
而且往往无法恢复。要还原只还原你自己动过的文件。
不可修改的文件
Section titled “不可修改的文件”官方会列出一批不可修改的文件(评测目录、评测 CLI、对应的 CMakeLists 等)。 这些文件改了就等于成绩无效,即使你只是「临时改一下做个实验」也很容易忘记还原。
建议开工时先把它们的校验和记下来,收工时核对:
sha256sum <官方列出的每个不可修改文件> > /tmp/readonly.sha256# 收工sha256sum -c /tmp/readonly.sha256评测配置文件如果在不可修改目录内,就按原样使用;需要改环境相关项时, 复制一份到目录外,用官方提供的环境变量指向你的副本(见 §5.2)。
5. 跑官方评测
Section titled “5. 跑官方评测”以下命令都在项目根目录执行。具体参数名以你拿到的 run_all.sh --help 和官方说明为准,
这里给的是通用用法。
5.1 基本调用
Section titled “5.1 基本调用”./evaluation/run_all.sh --tasks <任务名>常用参数:
| 参数 | 作用 |
|---|---|
--tasks a,b |
只跑指定任务,逗号分隔 |
--smoke N |
只跑 N 个样本,快速验证链路是否通 |
--no-build |
跳过构建,直接用现有二进制 |
第一次跑一定先用 --smoke 小样本把链路走通,确认能出数再上全量。全量动辄几小时,
在一个链路没通的配置上跑全量是纯浪费。
5.2 用自己的配置副本
Section titled “5.2 用自己的配置副本”不要在官方配置上反复 sed -i 改来改去,很容易改乱且不可追溯。每个实验复制一份:
cp evaluation/config.env <PROJECT_ROOT>/my_config.env# 只改环境相关项,比如设备编号、路径EVAL_CONFIG=<PROJECT_ROOT>/my_config.env ./evaluation/run_all.sh --tasks <任务名>写配置和校验配置要用同一种解析方式。 常见错误是用 shell 写、用自制正则读, 引号和空格的差异会让你以为配置生效了其实没有。正确做法是直接 source 出来看:
( set -a; source my_config.env; set +a; echo "DEVICE=$YOUR_DEVICE_VAR" )5.3 --no-build 什么时候能用
Section titled “5.3 --no-build 什么时候能用”只有当你确信当前二进制就是当前源码 + 当前编译选项构建出来的才能用。 下列任一情况都不能用:
- 刚改过源码
- 刚改过编译选项、CMakeLists、依赖库
- 不清楚现有二进制是谁、什么时候构建的
尤其注意:build/CMakeCache.txt 会保留上一次的配置项。如果你之前手动加过某个 cmake 选项,
之后即使用官方那条不带该选项的 cmake -B build -S .,缓存里的值仍然生效。
结果是源码看着是官方的、命令看着是官方的,二进制里却夹带着非官方的编译选项。
高价值的结论请用全新 build 目录复现,或者至少核对一遍:
grep -E '<你关心的选项名>|CMAKE_BUILD_TYPE' build/CMakeCache.txt5.4 产物在哪
Section titled “5.4 产物在哪”评测产物一般落在带时间戳的目录下:
evaluation/output/YYYYMMDD_HHMMSS/├── build.log├── <任务>.log├── metrics_<任务>.json└── ...metrics_<任务>.json 里通常有一个字段指向真正的详细报告路径。用它来定位报告,
不要用 ls -t | head -1 猜 —— 并发任务或上一轮的残留目录会让你选错,
而选错之后所有分析都建立在别人的数据上。
python3 -c "import json,sys; print(json.load(open(sys.argv[1]))['metrics']['report'])" \ evaluation/output/<时间戳>/metrics_<任务>.json如果报告尚未生成,可以在开跑前打一个时间标记,之后只找比它新的文件:
touch /tmp/run_start.marker# ... 跑评测 ...find evaluation -name '*report*.json' -newer /tmp/run_start.marker6. 长任务:怎么跑才不会被断线带走
Section titled “6. 长任务:怎么跑才不会被断线带走”全量评测可能几小时。绝对不要挂在前台 SSH 上。
6.1 推荐:setsid + nohup + 全重定向
Section titled “6.1 推荐:setsid + nohup + 全重定向”ssh myenv 'cd <PROJECT_ROOT> && \ setsid nohup ./evaluation/run_all.sh --tasks <任务名> \ > /tmp/job.log 2>&1 < /dev/null & \ sleep 2; echo LAUNCHED'三个要素缺一不可:
setsid让进程脱离当前会话,断线不会收到 SIGHUPnohup忽略挂断信号- stdin/stdout/stderr 全部重定向。只写
nohup cmd &是不够的:子进程仍持有 SSH 的管道, 会导致 SSH 迟迟不返回,或者断线后输出丢失
6.2 或者:tmux
Section titled “6.2 或者:tmux”ssh myenv -t 'tmux new -As work'# 在 tmux 里正常跑,Ctrl-B D 脱离,断线后重连接着看ssh myenv -t 'tmux attach -t work'tmux 适合交互式调试;setsid 适合脚本化批量任务。
6.3 查看进度
Section titled “6.3 查看进度”ssh myenv 'tail -50 /tmp/job.log'ssh myenv 'pgrep -af run_all.sh || echo NOT_RUNNING'不要只靠 ps | grep 判断,grep 会匹配到自己。用 pgrep -af 并确认匹配到的确实是你的进程。
7. 常见故障排查
Section titled “7. 常见故障排查”7.1 服务端启动超时
Section titled “7.1 服务端启动超时”评测会拉起一个推理服务再连它。如果报连接/读取超时,按顺序查:
npu-smi info # 卡上有没有别人的残留进程占着显存ss -ltnp | grep '<端口>' || true # 端口是否被占pgrep -af '<服务进程名>' || true # 有没有上一轮没退干净的服务最常见的原因就是上一轮的服务没退干净,占着端口和显存。清掉再跑通常就好了。
清理时不要用宽泛的 pkill -f,它可能匹配到你当前的 SSH 命令行、控制脚本本身,甚至别人的任务。
先列出来确认:
pgrep -af '<服务进程名>'kill <确认过的 PID>冷启动加载大模型权重本身也要时间,第一次跑慢是正常的;但如果每次都超时,那是残留没清干净。
7.2 进程退出码是 134,但结果其实是好的
Section titled “7.2 进程退出码是 134,但结果其实是好的”CANN 运行时在产物已经写完之后的清理阶段 abort 是常见现象,进程最终返回 134。 所以下面这种判断是错的:
if ./evaluation/run_all.sh --tasks <任务名>; then echo OK; else echo FAILED; fi # 不可靠正确做法是退出码和产物都记下来,以产物为准:
set +e./evaluation/run_all.sh --tasks <任务名> > run.log 2>&1rc=$?set -eecho "$rc" > run.rc
# 以产物判定if [ -f "<预期的产物路径>" ] && python3 -c "import json;json.load(open('<产物路径>'))"; then touch SUCCESSelse touch FAILEDfi退出码仍然要保留 —— 它能反映真正的运行期异常 —— 只是不能当作唯一依据。
7.3 报告生成了,不代表这一轮有效
Section titled “7.3 报告生成了,不代表这一轮有效”一次评测可能顺利结束、JSON 也能解析,但里面的样本数是 0。开始分析之前先确认这一轮跑完整了:
- 样本数 / 事件数是否等于预期
- 各类计数字段之间是否自洽
- 音频、文本等输出产物的数量是否对得上
- 日志里有没有大量报错被吞掉
跑通 ≠ 跑对。 把「这一轮是否有效」的判断写成脚本自动执行,比每次靠眼睛看可靠得多。
如果你要做前后对比,还要额外确认两次跑的工作量是一样的(样本数、生成长度等计数字段相等)。 计数不同就说明两次做的事不同,速度对比没有意义。
7.4 设备编号:进程外和进程内不是一回事
Section titled “7.4 设备编号:进程外和进程内不是一回事”评测框架通常会给服务进程设置可见设备,例如:
env["ASCEND_RT_VISIBLE_DEVICES"] = str(gpu_id)这之后,进程内部只看得到一张卡,它的逻辑编号是 0。也就是说:
- 你在外面指定的是物理卡
gpu_id - 进程里代码写的
gpu:0指向的是那张物理卡 - 进程里写
gpu:1会指向一个不存在的设备
排查设备问题时不要看配置解析的打印,要看 backend 实例的日志(它会打印实际初始化了哪个后端),
再和 npu-smi info 里的进程分布对照。
7.5 资产缺失
Section titled “7.5 资产缺失”数据集、评分模型往往需要单独下载。链路跑不通时先确认资产是不是真的在:
ls -lL <数据集目录> # -L 跟随软链,避免把软链本身的大小当成文件大小du -sh <数据集目录>注意用 ls -lL:软链接本身只有几十字节,ls -l 看起来像是个空文件,容易误判成下载失败。
8. 写控制脚本时的通用坑
Section titled “8. 写控制脚本时的通用坑”建议控制脚本开头加 set -Eeuo pipefail,但必须知道哪些正常行为会返回非零,
在 set -e 下直接把整个脚本杀掉:
| 写法 | 问题 |
|---|---|
n=$(grep -c PAT f) |
零匹配时打印 0 但退出码是 1,脚本当场退出 |
grep -q PAT f 裸写 |
没匹配到就退出 |
diff a b / cmp a b |
文件不同返回 1 |
kill -0 $pid |
进程不存在返回非零 |
find ... | grep ... | head -1 |
pipefail 下 head 提前关管道会让上游收到 SIGPIPE |
read var |
读到 EOF 返回非零 |
| 函数最后一条命令返回非零 | 整个函数返回非零 |
安全写法:
n=$(grep -c PAT f || true)
if grep -q PAT f; then echo 有; else echo 无; fi
set +esome_commandrc=$?set -e另外,CANN 的环境脚本可能引用未定义变量,在 set -u 下会直接失败:
set +usource /usr/local/Ascend/ascend-toolkit/set_env.shset -u9. 改动的可恢复性
Section titled “9. 改动的可恢复性”只要你动了源码或二进制,就必须保证能原样还原。推荐骨架:
#!/usr/bin/env bashset -Eeuo pipefailROOT=<PROJECT_ROOT>FILES=("$ROOT/path/to/file1" "$ROOT/path/to/file2")
cd "$ROOT"sha256sum "${FILES[@]}" > /tmp/before.sha256git status --porcelain > /tmp/before.status
restore() { local rc=$? trap - EXIT INT TERM set +e git checkout -- "${FILES[@]}" 2>/dev/null # 如果改动会影响二进制,这里要重新构建回原样 git status --porcelain > /tmp/after.status if cmp -s /tmp/before.status /tmp/after.status; then echo RESTORE_OK else echo RESTORE_FAILED >&2 diff /tmp/before.status /tmp/after.status fi exit "$rc"}trap restore EXIT INT TERM
# 实验主体要点:
- trap 一进来先保存
$?,再取消自身 trap 防止递归 - handler 内部
set +e,否则第一个还原动作失败就还原不了后面的 - 还原后用
git status --porcelain前后对比来确认,比人眼可靠 - 还原失败要显式报错,它的优先级高于实验结果本身
如果实验会新建文件,单独记录清单,还原时只删记录过的文件,不要用通配符扫目录。
10. 资源使用
Section titled “10. 资源使用”10.1 编译并行度
Section titled “10.1 编译并行度”nproc # 先看有多少核cmake --build build -j 32 # 日常增量构建核多不等于可以拉满。机器上可能还有别人的任务,-j$(nproc) 在几百核的机器上会把内存和 I/O 打爆。
先看负载再决定。
10.2 不要一边编译一边测性能
Section titled “10.2 不要一边编译一边测性能”编译会占满 CPU、内存带宽、页缓存和磁盘 I/O。即使 NPU 是空的,也会显著影响:
- 服务启动时间
- Python / CANN 初始化
- 数据加载
- 端到端延迟
性能测量期间不要并发编译、跑另一个评测、做 profile,或者跑大规模递归文件扫描。
10.3 开跑前看一眼负载
Section titled “10.3 开跑前看一眼负载”cat /proc/loadavgnpu-smi info负载明显偏高时等一等再跑。在忙碌的机器上测出来的数字既不能用来做决策, 也不能用来和之前的结果比较。
10.4 关于重复测量
Section titled “10.4 关于重复测量”同一个配置在不同时间跑出来的结果会有差异,服务重启前后尤其明显(缓存冷热、CPU 调度、 其它任务的干扰都会影响)。一次测量不能说明问题。 任何你打算据以做决定的数字,都应该在不同时间重复测几次,看它稳不稳定。
11. 检查清单
Section titled “11. 检查清单”- 确认连的是正确的机器
-
npu-smi info卡上没有残留进程 - 端口没被占
- 负载正常,没有并发的编译 / 评测
- 记录
git rev-parse HEAD、git status --porcelain、不可修改文件的 sha256
- 确认改动真的进了正在执行的那条路径 —— 编译成功不等于生效, 要有运行期证据(日志字段、符号表、计数器)
- 核对
build/CMakeCache.txt没有夹带上一轮的选项 - 二进制 sha256 有变化(没变说明根本没重新构建)
- 不只看退出码,看产物
- 报告能解析,且样本数 / 事件数符合预期
- 日志里没有被吞掉的错误
- 若做对比,两次的工作量计数字段相等
- 还原所有改过的文件,并用 sha256 /
git status核对 - 不可修改文件的 sha256 与开工时一致
- 服务进程已退出,卡上没有残留
- 还原失败时优先报错,不要带着未还原的工作树离开
附:一条能直接抄的最小工作流
Section titled “附:一条能直接抄的最小工作流”# 0. 配好 ~/.ssh/config 里的 myenv(见 1.3)
# 1. 环境自检ssh myenv 'hostname; npu-smi info; nproc; df -h /workspace'
# 2. 小样本跑通链路ssh myenv 'cd <PROJECT_ROOT> && \ setsid nohup ./evaluation/run_all.sh --tasks <任务名> --smoke 2 \ > /tmp/smoke.log 2>&1 < /dev/null & sleep 2; echo LAUNCHED'
# 3. 看进度ssh myenv 'tail -40 /tmp/smoke.log'
# 4. 找报告ssh myenv 'ls -td <PROJECT_ROOT>/evaluation/output/*/ | head -1'
# 5. 确认这一轮有效(样本数、事件数是否符合预期)ssh myenv 'python3 -m json.tool <报告路径> | head -40'
# 6. 链路通了再上全量,同样用 setsid 起祝顺利。