容器环境准备
镜像版本列表
| vLLM-MUSA 版本 | 服务器 | 镜像源 |
|---|---|---|
| 0.9.2 RC 3 | Intel | sh-harbor.mthreads.com/mcc-ai-serving/vllm-musa-ph1-py310-sets2:4.3.1-fix-ca-reduce |
| 0.9.2 RC 3 | Hygon | sh-harbor.mthreads.com/mcctest/vllm_musa:20251104_hygon |
创建容器
docker run -itd --name=${CONTAINER_NAME} \
-w /data --net=host --shm-size=500g \
-v /data:/data \
--env USE_CUSTOM_ALLREDUCE=1 \
--env MTHREADS_VISIBLE_DEVICES=all ${IMAGE_URL} sleep infinity
参数说明:
\--name=${CONTAINER\_NAME} 容器名称(可修改)
\-w /data 当前工作目录,后续性能数据会保存在这里。
\--net=host 多机分布式训练推理时,需要通信端口,将容器端口映射至宿主机方便操作。
\--shm-size=500g 模型训练时,需要设置否则会报错。
\-v /data:/data 映射宿主机路径至容器内,方便后续访问模型权重、测试代码等文件
\--env USE\_CUSTOM\_ALLREDUCE=1
\--env MTHREADS\_VISIBLE\_DEVICES=all 是借助MT Container Toolkis 插件将摩尔线程GPU设备映射至容器
${IMAGE\_URL} 是容器镜像URL地址,与1 章 环境准备中,vllm-musa镜像版本一致。
sleep infinity 容器启动后处于休眠状态避免容器退出。
例如:
MUSA SDK 为4.3.1版的 Intel 服务器,创建容器命令如下:
docker create \
--name sets-vllm \
-w /data \
--net host \
--shm-size=500g -v /data:/data \
--env USE_CUSTOM_ALLREDUCE=1 \
--env MTHREADS_VISIBLE_DEVICES=all \
sh-harbor.mthreads.com/mcc-ai-serving/vllm-musa-ph1-py310-sets2:4.3.1-fix-ca-reduce \
sleep infinity
MUSA SDK 为4.3.1版的 Hygon 服务器,创建容器命令如下:
docker create \
--name sets-vllm \
-w /data --net host \
--shm-size=500g -v /data:/data \
--env USE_CUSTOM_ALLREDUCE=1 \
--env MTHREADS_VISIBLE_DEVICES=all \
sh-harbor.mthreads.com/mcctest/vllm_musa:20251104_hygon \
sleep infinity
启动容器
执行命令 docker start ${CONTAINER_NAME} ,其中,${CONTAINER\_NAME} 与 3.1节创建的容器名一致。
例如,MUSA SDK 为4.3.1版的服务器,执行如下命令启动容器:
docker start sets-vllm
启动推理服务
启动会话
执行命令screen -S ${SESSION_NAME},创建并进入一个名为${SESSION\_NAME}的新会话。
命令说明
screen是一个终端复用工具,允许在单个终端窗口中创建多个独立的会话,即使断开连接,会话中的程序也能在后台继续运行。
\-S 用于指定新会话的名称
运行示例
例如,执行如下命令创建并进入新会话:
screen -S vllm-serve
启动推理服务
执行命令docker exec -it bash ${CONTAINER_NAME} ${SETS}/infer/vllm-musa/run-vllm-musa.sh ${MODEL_PATH} ${TP},启动vLLM推理服务。
命令说明
${CONTAINER_NAME}容器名,与3.1节 创建的容器名一致
${SETS}/infer/vllm-musa/run-vllm-musa.shvLLM启动脚本
${MODEL_PATH}模型本地路径
${TP}为Tensor Parallelism数,推荐使用1,4,8(根据客户测试需求进行修改)。
脚本说明
run-vllm-musa.sh 脚本代码如下:
export MODEL_PATH=$1
export TP=$2
MODEL_NAME=$(basename "$MODEL_PATH" | tr '[:upper:]' '[:lower:]')
set -x
VLLM_USE_V1=0 vllm serve ${MODEL_PATH} \
--trust-remote-code \
--gpu-memory-utilization 0.7 \
--served-model-name ${MODEL_NAME} \
--block-size 64 \
--tensor-parallel-size ${TP} \
--pipeline-parallel-size 1 \
--compilation-config '{"cudagraph_capture_sizes": [1,2,3,4,5,6,7,8,10,12,14,16,18,20,24,28,30,32,50,64,100,128,256], "simple_cuda_graph": true}'
set +x
其中, MODEL_PATH 为模型权重本地目录,与 第2章 下载模型权重保存的本地目录保持一致。
运行示例
例如,启动4个GPU推理服务:
docker exec -it sets-vllm bash /data/sanity-check/SETS/infer/vllm-musa/run-vllm-musa.sh /data/playground/model/qwen/qwen2.5-72b 4
vLLM启动成功后,日志如下所示:

- 脱离服务会话
同时按ctrl键和a键后,再按d键
运行测试脚本
需保持run-vllm-musa.sh启动(Application startup complete)后另起terminal执行vllm-perf-test.sh
启动会话
执行命令screen -S ${SESSION_NAME},创建并进入一个名为${SESSION\_NAME}的新会话。
命令说明
screen是一个终端复用工具,允许在单个终端窗口中创建多个独立的会话,即使断开连接,会话中的程序也能在后台继续运行。
-S 用于指定新会话的名称
运行示例
例如,执行如下命令创建并进入新会话:
screen -S vllm-bench
运行测试脚本
执行命令docker exec -it ${CONTAINER_NAME} ${SETS}/infer/vllm-musa/vllm-perf-test.sh ${MODEL_PATH} ${TP},运行测试脚本。
命令说明
${CONTAINER_NAME}容器名,与3.1节 创建的容器名一致
${SETS}/infer/vllm-musa/vllm-perf-test.sh测试脚本
${MODEL_PATH}模型本地路径,与4.2.2节一致
${TP}为Tensor Parallelism数,推荐使用1,4,8(根据客户测试需求进行修改)。保持与4.2.2节一致
脚本说明
vllm-perf-test.sh 脚本如下:
#!/bin/bash
set -euo pipefail
export MODEL_PATH=$1
export TP=$2
# Input/output length pairs
IO_PAIRS=(
"256 256"
"512 512"
"1024 1024"
"2048 1024"
"3072 1024"
"4096 1024"
)
# Concurrency and num-prompts (1-to-1 mapping)
CONCURRENCY_AND_PROMPTS=(
"1 2"
"2 4"
"4 8"
"8 16"
"16 32"
"32 64"
"50 100"
"64 128"
"100 200"
"128 256"
"256 512"
)
DATASET_NAME="random"
MODEL_NAME=$(basename "${MODEL_PATH%/}" | tr '[:upper:]' '[:lower:]')
CURRENT_TIME=$(TZ='Asia/Shanghai' date +%Y%m%d%H%M%S)
OUTPUT_NAME="vllm_gpu${TP}_${MODEL_NAME}_${CURRENT_TIME}_results"
OUTPUT_FILE=${OUTPUT_NAME}".csv"
RESULT_DIR=${OUTPUT_NAME}
mkdir -p ${RESULT_DIR}
# Ensure output directories exist
FULL_OUTPUT_PATH=${OUTPUT_FILE}
echo "" > "$FULL_OUTPUT_PATH" # Clear file if exists
# CSV header (with P95 fields included)
HEADER="input_len,output_len,max_concurrency,num_prompts,Successful_requests,Benchmark_duration_s,Total_input_tokens,Total_generated_tokens,Request_throughput_req_s,Output_token_throughput_tok_s,Total_Token_throughput_tok_s,Mean_TTFT_ms,Median_TTFT_ms,P95_TTFT_ms,P99_TTFT_ms,Mean_TPOT_ms,Median_TPOT_ms,P95_TPOT_ms,P99_TPOT_ms,Mean_ITL_ms,Median_ITL_ms,P95_ITL_ms,P99_ITL_ms,Mean_E2EL_ms,Median_E2EL_ms,P95_E2EL_ms,P99_E2EL_ms"
echo "$HEADER" >> "$FULL_OUTPUT_PATH"
echo "📊 Benchmark started for model: $MODEL_NAME"
echo "📂 Saving CSV results to: $FULL_OUTPUT_PATH"
# Loop through IO_PAIRS
for PAIR in "${IO_PAIRS[@]}"; do
read W O <<< "$PAIR"
# Loop through concurrency and corresponding num-prompts
for CP in "${CONCURRENCY_AND_PROMPTS[@]}"; do
read C N <<< "$CP"
echo "===== Running benchmark: input_len=$W, output_len=$O, max_concurrency=$C, num_prompts=$N ====="
RESULT=$(vllm bench serve \
--model "$MODEL_PATH" \
--trust-remote-code \
--served-model-name "$MODEL_NAME" \
--dataset-name "$DATASET_NAME" \
--random-input-len "$W" \
--random-output-len "$O" \
--num-prompts "$N" \
--ignore-eos \
--save-result \
--percentile-metrics 'ttft,tpot,itl,e2el' \
--metric-percentiles "95,99" \
--result-dir "$RESULT_DIR" \
--max-concurrency "$C" \
2>&1 | awk '/============ Serving Benchmark Result ============/{flag=1; next} /==================================================/{flag=0} flag')
CSV_LINE=$(echo "$RESULT" | awk '
function clean(s){gsub(/^[ \t]+|[ \t]+$/,"",s); return s}
/Successful requests:/ {sr=clean($NF)}
/Benchmark duration/ {bd=clean($NF)}
/Total input tokens/ {ti=clean($NF)}
/Total generated tokens/ {tg=clean($NF)}
/Request throughput/ {rt=clean($NF)}
/Output token throughput/ {ot=clean($NF)}
/Total Token throughput/ {tt=clean($NF)}
/Mean TTFT/ {mttft=clean($NF)}
/Median TTFT/ {medttft=clean($NF)}
/P95 TTFT/ {p95ttft=clean($NF)}
/P99 TTFT/ {p99ttft=clean($NF)}
/Mean TPOT/ {mtpot=clean($NF)}
/Median TPOT/ {medtpot=clean($NF)}
/P95 TPOT/ {p95tpot=clean($NF)}
/P99 TPOT/ {p99tpot=clean($NF)}
/Mean ITL/ {mitl=clean($NF)}
/Median ITL/ {meditl=clean($NF)}
/P95 ITL/ {p95itl=clean($NF)}
/P99 ITL/ {p99itl=clean($NF)}
/Mean E2EL/ {me2el=clean($NF)}
/Median E2EL/ {mede2el=clean($NF)}
/P95 E2EL/ {p95e2el=clean($NF)}
/P99 E2EL/ {p99e2el=clean($NF)}
END {
printf "%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s\n",
"'$W'","'$O'","'$C'","'$N'",
sr, bd, ti, tg, rt, ot, tt,
mttft, medttft, p95ttft, p99ttft,
mtpot, medtpot, p95tpot, p99tpot,
mitl, meditl, p95itl, p99itl,
me2el, mede2el, p95e2el, p99e2el
}'
)
echo "$CSV_LINE" >> "$FULL_OUTPUT_PATH"
MEAN_E2EL=$(echo "$CSV_LINE" | cut -d',' -f25)
echo "✅ Completed: input_len=$W, output_len=$O, max_concurrency=$C, num_prompts=$N, Mean_E2EL_ms=$MEAN_E2EL"
echo
done
done
echo "🎉 All benchmarks finished!"
echo "📊 Final CSV: $FULL_OUTPUT_PATH"
测试代码是按照不同的input、 output、 batch size 等条件循环调用vllm 内部测试工具 vllm bench serve 完成测试。
MODEL\_PATH 与 4.4.2 节的 MODEL\_PATH 保持一致。
TP 为 Tensor Parallelism 数,与4.4.2 节保持一致。
DATASET\_NAME 数据集采取随机生成token。
IO\_PAIRS 表示 输入长度 和 输出长度,目前标准测试共6组。输入输出长度如下表所示:
| input token length | output token length |
|---|---|
| 256 | 256 |
| 512 | 512 |
| 1024 | 1024 |
| 2048 | 1024 |
| 3072 | 1024 |
| 4096 | 1024 |
CONCURRENCY\_AND\_PROMPTS 表示并发数目和请求总数目,为保证推理服务处于最佳性能,建议并发数量与总请求数的比例关系为1:2。
如上所示,标准测试示例共2 * 6 * 11 = 132 个。
运行示例
docker exec -it sets-vllm bash /data/sanity-check/SETS/infer/vllm-musa/vllm-perf-test.sh /data/playground/model/qwen/qwen2.5-72b 4
性能数据
性能数据文件
在工作目录(3.1.节 -w 参数指定)下会生成 vllm\_gpu${TP}\_${MODEL\_NAME}\_${CURRENT\_TIME}\_results.csv的文件,此文件为测试性能的数据文件
测试项日志文件
在工作目录下会生成 vllm\_gpu${TP}\_${MODEL\_NAME}\_${CURRENT\_TIME}\_results目录,此目录下有名字如:openai-infqps-concurrency${NUM}-${MODEL\_NAME}-${DATE}-${TIME}.json的多个文件,每个文件为每条测试项的日志文件
示例

建议测试完成后把所有生成文件打包拷贝带走,以便后续使用。

