记录在两台910B4服务器上运行 W8A8 量化的 DeepSeek-R1-0528 模型,并使用 EvalScope 做一个简单的推理性能测试,后面还有一些推理性能相关的参数介绍以及模型报错排查方法

一、环境信息

模型DeepSeek-R1-0528-W8A8-MindIE需要是W8A8量化过的模型,每台机器上都要存放模型权重文件
服务器型号Atlas 800I A22台
显卡910B464G/张,2台共16张卡
MindIE>=2.1.RC1低于这个版本会出现对话时模型输出异常的问题
驱动>=24.1.0

1.1 下载模型权重文件

模型权重文件大概660G,需要确保服务器磁盘空间足够

从 ModelScope 上下载模型文件需要使用到modelscope工具

# 1、下载 modelscope
pip install modelscope

# 2、下载模型
modelscope download --model 'TensorLake/DeepSeek-R1-0528-W8A8-MindIE' --local_dir '/model/DeepSeek-R1-0528-W8A8-MindIE'

二、运行模型

2.1 准备 rank table 文件

使用多机推理时,需要将包含设备ip,服务器ip等信息的json文件地址传递给底层通信算子。

参考如下格式,配置 /model/rank_table_file.json (后面会将/model目录挂载到 MindIE 容器内),参考:

  • server_count:总节点数
  • server_list:节点列表,第一个server为主节点
  • server_id:当前节点的ip地址
  • container_ip:容器ip地址(服务化部署时需要),若无特殊配置,则与server_id相同
  • device_id:当前卡的本机编号,取值范围[0, 本机卡数)
  • device_ip:当前卡的ip地址,可通过hccn_tool命令获取,参考命令for i in {0..7};do hccn_tool -i $i -ip -g; done
  • rank_id:当前卡的全局编号,取值范围[0, 总卡数)
{
    "version": "1.0",
    "server_count": "2",
    "server_list": [
        {
            "server_id": "Master节点IP地址",
            "container_ip": "Master节点容器IP地址",
            "device": [
                { "device_id": "0", "device_ip": "10.20.0.2", "rank_id": "0" }, 
                { "device_id": "1", "device_ip": "10.20.0.3", "rank_id": "1" },
                { "device_id": "2", "device_ip": "10.20.0.4", "rank_id": "2" },
                { "device_id": "3", "device_ip": "10.20.0.5", "rank_id": "3" },
                { "device_id": "4", "device_ip": "10.20.0.6", "rank_id": "4" },
                { "device_id": "5", "device_ip": "10.20.0.7", "rank_id": "5" },
                { "device_id": "6", "device_ip": "10.20.0.8", "rank_id": "6" },
                { "device_id": "7", "device_ip": "10.20.0.9", "rank_id": "7" }
            ]
        },
        {
            "server_id": "Slave节点IP地址",
            "container_ip": "Slave节点容器IP地址",
            "device": [
                { "device_id": "0", "device_ip": "10.20.0.10", "rank_id": "8" },
                { "device_id": "1", "device_ip": "10.20.0.11", "rank_id": "9" },
                { "device_id": "2", "device_ip": "10.20.0.12", "rank_id": "10" },
                { "device_id": "3", "device_ip": "10.20.0.13", "rank_id": "11" },
                { "device_id": "4", "device_ip": "10.20.0.14", "rank_id": "12" },
                { "device_id": "5", "device_ip": "10.20.0.15", "rank_id": "13" },
                { "device_id": "6", "device_ip": "10.20.0.16", "rank_id": "14" },
                { "device_id": "7", "device_ip": "10.20.0.17", "rank_id": "15" }
            ]
        }
    ],
    "status": "completed"
}

2.2 运行 MindIE 容器

需要在两个节点上运行这个容器,随后会运行容器之后进入到容器内修改 MindIE 相关配置

参数说明:

  • --device=/dev/davinciN:挂载显卡设备,这里将8张卡全部挂载到容器内
  • --net=host:使用网络模式为host
  • -v /model:/model:挂载宿主机上的模型权重文件到容器内,需要自行修改
CONTAINER_NAME="deepseek-r1-0528"
MINDIE_IMAGE="swr.cn-south-1.myhuaweicloud.com/ascendhub/mindie:2.1.RC1-800I-A2-py311-openeuler24.03-lts"

docker run -itd --privileged --name=${CONTAINER_NAME} --net=host --shm-size=500g \
    --device=/dev/davinci0 \
    --device=/dev/davinci1 \
    --device=/dev/davinci2 \
    --device=/dev/davinci3 \
    --device=/dev/davinci4 \
    --device=/dev/davinci5 \
    --device=/dev/davinci6 \
    --device=/dev/davinci7 \
    --device=/dev/davinci_manager \
    --device=/dev/devmm_svm \
    --device=/dev/hisi_hdc \
    -v /usr/local/Ascend/driver:/usr/local/Ascend/driver \
    -v /usr/local/Ascend/add-ons/:/usr/local/Ascend/add-ons/ \
    -v /usr/local/sbin/:/usr/local/sbin/ \
    -v /etc/hccn.conf:/etc/hccn.conf \
    -v /model:/model \
    ${MINDIE_IMAGE} \
    /bin/bash

# 在容器内查看显卡信息
docker exec -it ${CONTAINER_NAME} npu-smi info

2.3 修改 MindIE 配置

修改两台机器上 MindIE 容器的配置,除ipAddress外其他配置都保持一致

# 进入 MindIE 容器
docker exec -it deepseek-r1-0528 bash

# 在容器内修改 MindIE 配置文件
vim /usr/local/Ascend/mindie/latest/mindie-service/conf/config.json

配置文件参考如下:

https://www.hiascend.com/document/detail/zh/mindie/21RC1/mindiellm/llmdev/mindie_llm0004.html

  • ServerConfig:
    • ipAddress: 修改为容器/服务器IP地址(由于MindIE启动命令中指定了网络模式为 host,所以容器IP地址与服务器IP地址一致)
    • httpsEnabled: 配置为false
    • interCommTLSEnabled: 配置为false
  • BackendConfig:
    • multiNodesInferEnabled: 配置为true
    • interNodeTLSEnabled: 配置为false
    • npuDeviceIds: 使用NPU卡的索引,多张卡的索引号使用逗号分隔,比如[[0,1]]。多机推理场景下该值无效,每个节点上使用的npuDeviceIds根据ranktable计算获得。
    • modelName: 模型名称,比如deepseek-r4-0528,在后续调用模型需要在请求中填写该名称
    • modelWeightPath: 模型权重路径,比如/model/DeepSeek-R1-0528-W8A8-MindIE
    • worldSize: 模型参数中worldSize必须与使用的NPU数量相等。多机推理场景下该值无效,worldSize根据ranktable计算获得。
    • dp/tp/sp/moe_tp/moe_ep: 如果配置文件中没有这几个配置,需要手动添加(参考下面的配置文件)

性能相关参数,可修改这些参数调整服务化性能:

  • BackendConfig:
    • maxSeqLen: 最大序列长度
    • maxInputTokenLen: 最大输入 Token 长度
  • ScheduleConfig:
    • maxPrefillTokens: Prefill阶段,当前batch中所有input token总数。maxPrefillTokensmaxPrefillBatchSize谁先达到各自的取值就完成本次组batch。建议配置为maxInputTokenLen
    • maxPrefillBatchSize: Prefill阶段,一个batch可以组的最大请求数量。maxPrefillBatchSizemaxPrefillTokens谁先达到各自的取值就完成本次组batch。
    • maxIterTimes: 模型全局最大输出长度。会影响每个用户请求得到的模型实际输出长度outputLen,规则为:outputLen = min(maxIterTimes, max_tokens, maxSeqLen - inputLen)outputLen = min(maxIterTimes, max_new_tokens, maxSeqLen - inputLen)
{
    "Version" : "1.0.0",

    "ServerConfig" :
    {
        "ipAddress" : "10.1.2.10",
        "managementIpAddress" : "127.0.0.2",
        "port" : 1025,
        "managementPort" : 1026,
        "metricsPort" : 1027,
        "allowAllZeroIpListening" : true,
        "maxLinkNum" : 1000,
        "httpsEnabled" : false,
        "fullTextEnabled" : false,
        "tlsCaPath" : "security/ca/",
        "tlsCaFile" : ["ca.pem"],
        "tlsCert" : "security/certs/server.pem",
        "tlsPk" : "security/keys/server.key.pem",
        "tlsPkPwd" : "security/pass/key_pwd.txt",
        "tlsCrlPath" : "security/certs/",
        "tlsCrlFiles" : ["server_crl.pem"],
        "managementTlsCaFile" : ["management_ca.pem"],
        "managementTlsCert" : "security/certs/management/server.pem",
        "managementTlsPk" : "security/keys/management/server.key.pem",
        "managementTlsPkPwd" : "security/pass/management/key_pwd.txt",
        "managementTlsCrlPath" : "security/management/certs/",
        "managementTlsCrlFiles" : ["server_crl.pem"],
        "kmcKsfMaster" : "tools/pmt/master/ksfa",
        "kmcKsfStandby" : "tools/pmt/standby/ksfb",
        "inferMode" : "standard",
        "interCommTLSEnabled" : false,
        "interCommPort" : 1121,
        "interCommTlsCaPath" : "security/grpc/ca/",
        "interCommTlsCaFiles" : ["ca.pem"],
        "interCommTlsCert" : "security/grpc/certs/server.pem",
        "interCommPk" : "security/grpc/keys/server.key.pem",
        "interCommPkPwd" : "security/grpc/pass/key_pwd.txt",
        "interCommTlsCrlPath" : "security/grpc/certs/",
        "interCommTlsCrlFiles" : ["server_crl.pem"],
        "openAiSupport" : "vllm",
        "tokenTimeout" : 600,
        "e2eTimeout" : 600,
        "distDPServerEnabled":false
    },

    "BackendConfig" : {
        "backendName" : "mindieservice_llm_engine",
        "modelInstanceNumber" : 1,
        "npuDeviceIds" : [[0,1,2,3,4,5,6,7]],
        "tokenizerProcessNumber" : 8,
        "multiNodesInferEnabled" : true,
        "multiNodesInferPort" : 1120,
        "interNodeTLSEnabled" : false,
        "interNodeTlsCaPath" : "security/grpc/ca/",
        "interNodeTlsCaFiles" : ["ca.pem"],
        "interNodeTlsCert" : "security/grpc/certs/server.pem",
        "interNodeTlsPk" : "security/grpc/keys/server.key.pem",
        "interNodeTlsPkPwd" : "security/grpc/pass/mindie_server_key_pwd.txt",
        "interNodeTlsCrlPath" : "security/grpc/certs/",
        "interNodeTlsCrlFiles" : ["server_crl.pem"],
        "interNodeKmcKsfMaster" : "tools/pmt/master/ksfa",
        "interNodeKmcKsfStandby" : "tools/pmt/standby/ksfb",
        "ModelDeployConfig" :
        {
            "maxSeqLen" : 13300,
            "maxInputTokenLen" : 8300,
            "truncation" : false,
            "ModelConfig" : [
                {
                    "modelInstanceType" : "Standard",
                    "modelName" : "deepseek-r1-0528",
                    "modelWeightPath" : "/model/DeepSeek-R1-0528-W8A8-MindIE",
                    "worldSize" : 8,
                    "cpuMemSize" : 5,
                    "npuMemSize" : -1,
                    "backendType" : "atb",
                    "trustRemoteCode" : false,
                    "async_scheduler_wait_time": 120,
                    "kv_trans_timeout": 10,
                    "kv_link_timeout": 1080,
                    "dp": 2,
                    "tp": 8,
                    "sp": 1,
                    "moe_tp": 4,
                    "moe_ep": 4
                }
            ]
        },

        "ScheduleConfig" :
        {
            "templateType" : "Standard",
            "templateName" : "Standard_LLM",
            "cacheBlockSize" : 128,

            "maxPrefillBatchSize" : 50,
            "maxPrefillTokens" : 8300,
            "prefillTimeMsPerReq" : 150,
            "prefillPolicyType" : 0,

            "decodeTimeMsPerReq" : 50,
            "decodePolicyType" : 0,

            "maxBatchSize" : 4096,
            "maxIterTimes" : 5000,
            "maxPreemptCount" : 0,
            "supportSelectBatch" : false,
            "maxQueueDelayMicroseconds" : 5000
        }
    }
}

2.4 修改文件权限

需要修改两台机器上 MindIE 容器中一些文件的权限

# 进入 MindIE 容器
docker exec -it deepseek-r1-0528 bash


# 修改模型权重文件权限
chmod -R 750 /model/DeepSeek-R1-0528-W8A8-MindIE
# 修改 rank table 文件权限
chmod 640 /model/rank_table_file.json
# 修改配置文件权限
chmod 640 /usr/local/Ascend/mindie/latest/mindie-service/conf/config.json

2.5 运行 MindIE 服务

修改完配置之后就可以启动模型服务了,需要在两台节点上都要操作,注意修改MIES_CONTAINER_IP变量值为本机IP:

配置比较麻烦,可以自行修改成脚本自动拉起模型

# 进入 MindIE 容器
docker exec -it deepseek-r1-0528 bash


## 配置变量
export MIES_CONTAINER_IP=10.1.2.10 # 本机ip,两台机器不同
export WORLD_SIZE=16
export RANK_TABLE_FILE=/model/rank_table_file.json

source /usr/local/Ascend/ascend-toolkit/set_env.sh
source /usr/local/Ascend/mindie/set_env.sh
source /usr/local/Ascend/mindie/latest/mindie-service/set_env.sh
source /usr/local/Ascend/mindie/latest/mindie-llm/set_env.sh
source /usr/local/Ascend/nnal/atb/set_env.sh

# export MINDIE_LOG_LEVEL="info"
export MINDIE_LOG_TO_STDOUT=1
export ASDOPS_LOG_TO_STDOUT=1
export ASDOPS_LOG_LEVEL=ERROR
export ATB_LLM_HCCL_ENABLE=1
export ATB_LLM_COMM_BACKEND="hccl"
export HCCL_CONNECT_TIMEOUT=7200
export HCCL_EXEC_TIMEOUT=0
export PYTORCH_NPU_ALLOC_CONF="expandable_segments:True"
# 集合通信优化:AIV
export HCCL_OP_EXPANSION_MODE="AIV"
# 算子下发队列优化等级:2
export TASK_QUEUE_ENABLE=2
# CPU绑核:细粒度
export CPU_AFFINITY_CONF=2
# python高并发:10
export OMP_NUM_THREADS=10
# 内存最大使用比例:0.96,过大会有Failed to get engine response报错风险
export NPU_MEMORY_FRACTION=0.96

cd /usr/local/Ascend/mindie/latest/mindie-service
./bin/mindieservice_daemon

当两个 MindIE 服务都出现Daemon start success之后说明模型服务运行成功

2.6 验证模型服务

可以通过下面命令验证下模型服务是否可用,地址为主节点IP,如果可以正常获得结果,则说明模型运行成功

这里使用的IP是主节点 IP(也就是 rank table 中配置的第一个节点的 IP)

curl -X POST -H "Content-Type: application/json" -d '{
    "model": "deepseek-r1-0528",
    "messages": [{
        "role": "user",
        "content": "你好"
    }],
    "do_sample":true,
    "temperature":0.6,
    "top_p":0.95,
    "max_tokens": 2000,
    "stream": false
}' http://10.1.2.10:1025/v1/chat/completions

三、推理性能测试

测试工具选择 EvalScope,示例脚本如下:

EvalScope 支持命令行、Python两种执行方式,这两种方式对结果不会有影响,这里使用 Python 脚本的方式,再自行写脚本批量执行

# llm-bench.py
from evalscope.perf.main import run_perf_benchmark
from evalscope.perf.arguments import Arguments

task_cfg = Arguments(
    parallel=[1],
    number=[10],
    model='deepseek-r1-0528',
    url='http://127.0.0.1:1025/v1/chat/completions',
    api='openai',
    dataset='random',
    min_tokens=256,
    max_tokens=256,
    prefix_length=0,
    min_prompt_length=1024,
    max_prompt_length=1024,
    tokenizer_path='/model/DeepSeek-R1-0528-W8A8-MindIE',
    extra_args={'ignore_eos': True}
)

results = run_perf_benchmark(task_cfg)

执行:

python3 llm-bench.py

结果:

输入Token数输出Token数并发数请求数TTFT(s)TPOT(s)吞吐(tokens/s)测试持续时间(s)
102410241100.24980.079527.8786816.1595
1024102416501.01820.0847295.8321346.1467
1024102432641.93590.0909688.9966189.9679
10241024641283.05360.10211218.0585215.0697
10241024961924.14960.11831567.1657250.4576
102410241282565.32180.13451833.4286285.8392
102410241923847.61810.19151664.9904472.196
204820481100.36150.080127.67641644.1845
2048204816501.83910.0844296.3263689.7419
2048204832643.12260.0916687.6929381.1939
20482048641285.4360.10491189.3768440.2723
20482048961928.07330.14591100.0131724.744
2048204812825649.89690.16911125.1326931.6918
20482048192384169.46480.19911233.45751200.5783
409610241100.58610.080468.6732828.2852
4096102416503.22890.087708.3615361.2423
4096102432645.73490.09461598.6378204.9749
40961024641288.27580.13721880.7218348.3938
409610249619261.9660.14951955.4701522.4232
40961024128256123.9540.14971918.4845682.2142
40961024192384260.50850.15811902.05981032.7816

四、性能调优相关

可以根据自身环境进行调整测试寻求符合自身需求的配置

4.1 相关参数介绍

性能调优相关参数:

  • maxSeqLen: 最大序列长度

  • maxInputTokenLen: 最大输入 Token 长度

  • maxIterTimes: 模型全局最大输出长度。会影响每个用户请求得到的模型实际输出长度outputLen,规则为:outputLen = min(maxIterTimes, max_tokens, maxSeqLen - inputLen)outputLen = min(maxIterTimes, max_new_tokens, maxSeqLen - inputLen)

  • maxPrefillTokens: Prefill阶段,当前batch中所有input token总数。maxPrefillTokensmaxPrefillBatchSize谁先达到各自的取值就完成本次组batch。建议配置为maxInputTokenLen

  • maxPrefillBatchSize: Prefill阶段,一个batch可以组的最大请求数量。maxPrefillBatchSizemaxPrefillTokens谁先达到各自的取值就完成本次组batch。

一般配置为 maxSeqLen = maxInputTokenLen + maxIterTimes,并且 maxPrefillTokens = maxInputTokenLen

maxPrefillBatchSizemaxPrefillTokens共同作用,控制输入请求的序列总长度,由于最大输入的要求,maxPrefillTokens往往设置与最大输入相同,可以保证最长的请求也可以正常推理,若实际输入长度远低于最大输入长度,maxPrefillBatchSize会存在一定的调优空间

4.2 性能数据分析

在启动服务化启动前导入以下环境变量,可以开启调度日志的打印,以及对应耗时信息

export MINDIE_LLM_BENCHMARK_ENABLE=2

最终调度日志会存放在以下路径/usr/local/Ascend/mindie/latest/mindie-llm/logs/benchmark.jsonl

五、运行失败排查

运行模型时可能会经常发生启动失败等问题,整理了一些常用的排查方法

5.1 驱动版本较老

如果使用的 MindIE 版本比较新,服务器上装的驱动版本比较老的话,可能会出现不兼容的情况,可以尝试更新驱动和固件的版本

5.2 配置错误

常见配置错误为:

  1. npuDeviceIds中配置的卡号的数量与worldSize不一致
  2. maxSeqLen等相关配置过大,超出显卡显存
  3. MindIE 配置文件、Rank table 文件、模型文件权限错误

5.3 查看 MindIE 运行日志

模型启动时配置日志相关环境变量

注意:正式运行模型服务时不建议设置日志级别为debugMINDIE_LOG_LEVEL="debug"),debug 级别输出的日志非常多而且不一定会有所帮助,在实际排查时可能还会更困难

# 修改日志级别(仅在排查时建议设置为 debug 级别)
export MINDIE_LOG_LEVEL="debug"
# 日志输出到标准输出
export MINDIE_LOG_TO_STDOUT=1

也可以在容器中的~/mindie/log中查看日志