vCANN-RT
介绍
vCANN-RT(virtual CANN Runtime)是ubs-virt-enpu提供NPU算力软切分的方案。本方案需要依赖Linux系统Preload Hook的能力,通过预加载软切分动态库,拦截部分runtime API的调用,根据算力和显存资源配额信息,进行算力控制或显存控制。
环境准备
软件版本
Atlas A2 推理系列产品
表 1 软件版本
| 软件 | 版本 |
|---|---|
| CANN | 8.5.0 、9.1.0 |
| HDK | 25.5.0及以上版本 |
| (可选)Kubernetes | 1.17.x~1.34.x,推荐使用1.19.x及以上版本。 (直接使用Docker部署则不需要) |
| (可选)MindCluster | 26.0.0(直接使用Docker部署则不需要) |
Atlas A3 推理系列产品
表 2 软件版本
| 软件 | 版本 |
|---|---|
| CANN | 8.5.0 、9.1.0 |
| HDK | 26.0.0及以上版本 |
| (可选)Kubernetes | 1.17.x~1.34.x,推荐使用1.19.x及以上版本。 (直接使用Docker部署则不需要) |
| (可选)MindCluster | 26.0.0(直接使用Docker部署则不需要) |
Ascend 950PR 产品
表 3 软件版本
| 软件 | 版本 |
|---|---|
| CANN | 9.1.0 |
| HDK | Atalas I 5.0.0 |
| (可选)Kubernetes | 1.17.x~1.34.x,推荐使用1.19.x及以上版本。 (直接使用Docker部署则不需要) |
| (可选)MindCluster | 26.1.0(直接使用Docker部署则不需要) |
主机侧环境配置
主机侧通过npu-smi工具开启容器共享模式,可支持多个容器挂载同一设备。若设备未开启容器共享模式,则只能挂载到单个容器。若配合MindCluster使用,要求整节点开启容器共享模式。
# Atlas A2 / A3 推理系列产品:设置容器共享模式
npu-smi set -t device-share -i ${id} -c ${chip_id} -d ${value}
# Ascend 950PR 产品:设置容器共享模式
npu-smi set -t device-share -i ${id} -d ${value}
# 查询设备容器共享模式
npu-smi info -t device-share -i ${id}
表 4 参数说明
| 参数 | 参数选项 | 说明 |
|---|---|---|
| id | 设备id | 通过npu-smi info -l命令查询获取的NPU ID即为设备id。 |
| chip_id | 芯片id | 通过npu-smi info -m命令查询获取的Chip ID即为芯片id。 |
| value |
|
容器共享模式使能状态。 |
示例: 开启设备0所有芯片的容器共享模式,查询设备0容器共享模式。
npu-smi set -t device-share -i 0 -d 1
npu-smi info -t device-share -i 0
主机侧可设置容器共享持久化使能状态。若持久化功能为开启状态,则重启系统后设备的容器共享模式使能状态与重启前保持一致。
npu-smi set -t device-share-cfg-recover -d ${value}
表 5 参数说明
| 参数 | 参数选项 | 说明 |
|---|---|---|
| value |
|
容器共享持久化使能状态。 |
(可选)针对A3 推理系列产品,如采用下文提到的docker方式部署,则在主机侧需要通过npu-smi工具开启支持单Die通入容器的模式。
npu-smi set -t multi-die-policy -d ${value}
表 6 参数说明
| 参数 | 参数选项 | 说明 |
|---|---|---|
| value |
|
A3的device通入容器的策略模式 |
虚拟化环境检测工具准备
用户需要保证业务容器/usr/bin目录内包含systemd-detect-virt命令行工具,该工具用于检测系统的运行环境是否为虚拟化环境和虚拟化方式。若容器内未安装systemd-detect-virt工具,软切分动态库在调用dcmi接口时会出现故障。
源码获取
可以使用如下方式获取vCANN-RT源码。
git clone <ubs-virt-enpu-vcann-rt-url>
源码目录结构
vCANN-RT源码的主要目录结构如下所示:
.
├── scripts // 存放项目中编译构建使用的脚本文件
├── src // 存放项目的功能实现源码,仅该目录参与构建出包
└── test // 存放项目的ut用例
编译
在编译vCANN-RT源码前,用户需要设置CANN的环境变量。如果驱动安装在非默认路径,还需要设置驱动路径环境变量。
vCANN-RT在代码仓中提供了统一的编译构建脚本(即make_build.sh文件),可以直接执行该脚本文件进行编译构建。
bash make_build.sh
编译完成之后,会在build目录下面产生相应的编译产物。
部署
用户可以选择将软切分动态库libvruntime.so和检测工具enpu-monitor分别拷贝到/opt/enpu/vcann-rt/lib和/opt/enpu/vcann-rt/tools目录。(如果选择不拷贝,则需要在后面的流程中修改相应的路径。)
配置preload文件
创建ld.so.preload文件,并将libvruntime.so的路径配置到ld.so.preload文件中,用于预加载软切分动态库。
vi ld.so.preload
# 例如libvruntime.so 的路径为/opt/enpu/vcann-rt/lib
/opt/enpu/vcann-rt/lib/libvruntime.so
主机侧ld.so.preload文件的路径用户可自定义,文档后续内容中使用${preload_path}表示,容器内为固定路径/etc/ld.so.preload。不建议将ld.so.preload文件放置在主机的/etc目录,否则将在主机侧预加载软切分动态库,可能影响主机侧业务。
启动业务容器
vCANN-RT支持两种方式启动业务容器:
方式一:基于kubernetes编排系统部署
用户通过k8s yaml文件,在申请容器时声明所需的vNPU算力资源百分比例和显存资源数量。k8s会将容器调度到资源充足的节点,并将算力和显存资源配额等信息以配置文件挂载到容器内。同时,k8s会将vCANN-RT的包挂载到容器,容器内即可使能vCANN-RT软切分能力。
对于Atlas A2 / A3 推理系列产品,yaml配置文件的格式可参考如下示例文件vnpu-base.yaml:
apiVersion: mindxdl.gitee.com/v1
kind: AscendJob
metadata:
# 容器名, 建议与yaml文件名一致
name: vnpu-base
# namespace
namespace: vnpu
labels:
framework: pytorch
ring-controller.atlas: ascend-910b
fault-scheduling: "force"
fault-retry-times: "10"
# 算力aicore配额,单位:%
huawei.com/scheduler.softShareDev.aicoreQuota: "20"
# 显存HBM配额,单位:MB
huawei.com/scheduler.softShareDev.hbmQuota: "60000"
# 调度策略,默认弹性模式(elastic),可选fixed-share,elastic,best-effort
huawei.com/scheduler.softShareDev.policy: "elastic"
annotations:
# 集群调度组件的调度策略
huawei.com/schedule_policy: "chip1-softShareDev"
spec:
# work when enableGangScheduling is true
schedulerName: volcano
runPolicy:
# work when enableGangScheduling is true
schedulingPolicy:
# soft share dev tasks with multiple replicas are only supported for single-machine scenarios.
minAvailable: 2
queue: default
successPolicy: AllWorkers
replicaSpecs:
Master:
replicas: 1
restartPolicy: Never
template:
metadata:
labels:
ring-controller.atlas: ascend-910b
spec:
automountServiceAccountToken: false
nodeSelector:
# 系统架构:huawei-arm / huawei-x86
host-arch: huawei-arm
# depend on your device model, 910bx8 is module-910b-8 ,910bx16 is module-910b-16
accelerator-type: module-910b-8
containers:
# do not modify
- name: ascend
# 镜像名称
image: ${image_name}
imagePullPolicy: IfNotPresent
env:
# IP address of the physical node, which is used to identify the node where the pod is running
- name: XDL_IP
valueFrom:
fieldRef:
fieldPath: status.hostIP
command:
- /bin/bash
- -c
args: [ "sleep 3000000;" ]
ports:
# default value containerPort: 2222 name: ascendjob-port if not set
# determined by user
- containerPort: 2222
# do not modify
name: ascendjob-port
resources:
requests:
# should be equal to huawei.com/scheduler.softShareDev.aicoreQuota
huawei.com/Ascend910: 20
limits:
# should be equal to huawei.com/scheduler.softShareDev.aicoreQuota
huawei.com/Ascend910: 20
volumeMounts:
- name: sbin
mountPath: /usr/local/sbin/
- name: ascend-driver
mountPath: /usr/local/Ascend/driver
- name: dshm
mountPath: /dev/shm
- name: localtime
mountPath: /etc/localtime
# 软切分动态库路径
- name: libpreload
mountPath: /opt/enpu/vcann-rt/lib/libvruntime.so
# 监测工具路径
- name: tools
mountPath: /opt/enpu/vcann-rt/tools/enpu-monitor
# preload配置文件路径
- name: preload
mountPath: /etc/ld.so.preload
volumes:
- name: sbin
hostPath:
path: /usr/local/sbin/
- name: ascend-driver
hostPath:
path: /usr/local/Ascend/driver
- name: dshm
emptyDir:
medium: Memory
- name: localtime
hostPath:
path: /etc/localtime
# 软切分动态库路径
- name: libpreload
hostPath:
path: /opt/enpu/vcann-rt/lib/libvruntime.so
# 监测工具路径
- name: tools
hostPath:
path: /opt/enpu/vcann-rt/tools/enpu-monitor
# preload配置文件路径
- name: preload
hostPath:
path: ${preload_path}/ld.so.preload
Worker:
replicas: 1
restartPolicy: Never
template:
metadata:
labels:
ring-controller.atlas: ascend-910b
spec:
automountServiceAccountToken: false
nodeSelector:
# 系统架构:huawei-arm / huawei-x86
host-arch: huawei-arm
# depend on your device model, 910bx8 is module-910b-8 ,910bx16 is module-910b-16
accelerator-type: module-910b-8
containers:
# do not modify
- name: ascend
image: ${image_name}
imagePullPolicy: IfNotPresent
env:
- name: XDL_IP
valueFrom:
fieldRef:
fieldPath: status.hostIP
command:
- /bin/bash
- -c
args: [ "sleep 3000000;" ]
ports:
- containerPort: 2222
# do not modify
name: ascendjob-port
resources:
requests:
# should be equal to huawei.com/scheduler.softShareDev.aicoreQuota
huawei.com/Ascend910: 20
limits:
# should be equal to huawei.com/scheduler.softShareDev.aicoreQuota
huawei.com/Ascend910: 20
volumeMounts:
- name: sbin
mountPath: /usr/local/sbin/
- name: ascend-driver
mountPath: /usr/local/Ascend/driver
- name: dshm
mountPath: /dev/shm
- name: localtime
mountPath: /etc/localtime
- name: libpreload
mountPath: /opt/enpu/vcann-rt/lib/libvruntime.so
- name: tools
mountPath: /opt/enpu/vcann-rt/tools/enpu-monitor
- name: preload
mountPath: /etc/ld.so.preload
volumes:
- name: sbin
hostPath:
path: /usr/local/sbin/
- name: ascend-driver
hostPath:
path: /usr/local/Ascend/driver
- name: dshm
emptyDir:
medium: Memory
- name: localtime
hostPath:
path: /etc/localtime
- name: libpreload
hostPath:
path: /opt/enpu/vcann-rt/lib/libvruntime.so
- name: tools
hostPath:
path: /opt/enpu/vcann-rt/tools/enpu-monitor
- name: preload
hostPath:
path: ${preload_path}/ld.so.preload
可根据实际业务进行参数配置:
-
metadata:
- name: 容器名,建议与yaml文件名一致。
- namespace: 命名空间。
- labels:
-
huawei.com/scheduler.softShareDev.aicoreQuota: 算力aicore配额(需配置为整数),单位:%
-
huawei.com/scheduler.softShareDev.hbmQuota: 显存HBM配额(需配置为整数),单位:MB
-
huawei.com/scheduler.softShareDev.policy: 调度策略,默认为弹性模式。
- 固定配额模式(fixed-share)
- 弹性模式(elastic)
- 争抢模式(best-effort)
-
每种模式的详细介绍参见表7,其中在争抢模式下,为充分利用算力资源,此时aicore的使用将不受配额的限制,但 HBM 的使用仍受配额的限制。
-
-
host-arch: 系统架构 huawei-arm / huawei-x86
-
containers:
- image: 镜像名。
-
volumes:
- name: libpreload
- hostPath:
- path: /opt/enpu/vcann-rt/lib/libvruntime.so # 软切分动态库路径,根据实际路径进行修改
- hostPath:
- name: tools
- hostPath:
- path: /opt/enpu/vcann-rt/tools/enpu-monitor # 检测工具路径,根据实际路径进行修改
- hostPath:
- name: preload # preload配置文件路径。
- hostPath:
- path: ${preload_path}/ld.so.preload # 步骤1中创建的ld.so.preload文件路径。
- hostPath:
- name: libpreload
对于Ascend 950PR 产品,yaml配置文件的格式需要做以下修改:
-
删除910b相关配置:ring-controller.atlas: ascend-910b 及 accelerator-type: module-910b-8
-
节点资源配置参数名修改为huawei.com/npu,示例如下
resources: requests: # should be equal to huawei.com/scheduler.softShareDev.aicoreQuota huawei.com/npu: 20 limits: # should be equal to huawei.com/scheduler.softShareDev.aicoreQuota huawei.com/npu: 20
| 模式名称 | 特点描述 |
|---|---|
| 固定配额模式(fixed-share) | 各vNPU按配比严格执行时间片。 a. 若有暂未分配的vNPU资源,则在最后一个vNPU时间片消耗完时,睡眠一段时间。此时NPU将闲置空转。 b. 某vNPU上无任务运行,仍会按照给定比例消耗完该vNPU的时间片。 |
| 弹性模式(elastic) | 由于固定配额模式可能出现NPU资源浪费,在此模式下,在各vNPU按配比执行时间片的基础上,为提高卡资源利用率,优化了调度逻辑。 a. 最后一个vNPU时间片消耗完,马上将NPU使用权释放给下一个vNPU,跳过睡眠逻辑。 b. 在某vNPU上无任务运行,则直接跳过未消耗完的时间片,切换至另一个vNPU。 c. 时间片借用机制:正在执行的vNPU在识别到卡资源空闲的情况下,允许其他vNPU借用该时间片内浪费的资源,使卡上资源不被浪费,进而提升整卡利用率。 |
| 争抢模式(best-effort) | vNPU之间自行争抢NPU资源,该模式的资源利用率最高,但无法保证vNPU的QoS。 |
使用yaml文件拉起容器:
kubectl apply -f ${container_name.yaml}
# 查看容器
kubectl get pods -n ${namespace}
如果容器启动失败,可参考方式二中的解决方法。
进入容器:
kubectl exec -it ${pod_name} -n ${namespace} bash
方式二:docker方式部署(不依赖kubernetes组件)
当不依赖k8s组件使用vCANN-RT时,需要用户在启动容器时自行挂载软切分相关动态库、文件和设备。例如软件分动态库、配置文件、共享内存设备和物理NPU设备。具体步骤如下:
-
创建vNPU配置文件和共享内存设备。
针对每个容器,需要在主机侧创建一个配置文件,并映射到容器的npu_info.config文件中(由于不同容器的配置内容不同,需要确保每个容器的配置文件在主机侧独立存储并明确区分,比如通过文件名或者路径区分)。配置文件的格式和字段示例如下:
physical-npu-id=0 virtual-npu-id=0 aicore-quota=20 memory-quota=1024 shm-id=xxx scheduling-policy=2表 8 配置项说明
参数 参数选项 说明 physical-npu-id 物理NPU id physical-npu-id=0表示使用第0张物理NPU。virtual-npu-id vNPU id 需要从0开始配置,并且同一个物理NPU下的vNPU不允许重复。 aicore-quota AI Core资源配额,单位为% 表示算力使用的时间比例,需配置为整数。假设当前每个time slice为100ms, 申请了20%的算力资源,那么该容器有20ms的NPU使用权。 memory-quota HBM资源配额,单位为MB 表示显存资源使用容量,需配置为整数。当前容器内所有进程使用的HBM总量不能超过HBM资源配额。 shm-id 共享内存文件名称 该文件名称采用物理NPU对应的VDie ID, 可以保证全局唯一。
通过npu-smi info -t board -i ${id} -c ${chip_id}命令查询VDie ID。
查询完成之后,可以通过-符号拼接成文件名称,例如:shm-id=11111111-22222222-33333333-44444444-55555555scheduling-policy - 默认配置为2。
- 1: 固定配额模式(fixed-share)
- 2: 弹性模式(elastic)
- 3: 争抢模式(best-effort)
调度策略(具体介绍可参见表7,其中在争抢模式下,为充分利用算力资源,此时aicore的使用将不受配额的限制,但 HBM 的使用仍受配额的限制)。 此外,需要设置配置文件具有合适的权限,建议为644。
-
启动业务容器。
用户在启动容器时,需要将软切分相关动态库、文件和设备挂载到容器(具体可参见表9),容器启动命令可参考(假如使用第0张NPU卡):
docker run -it --name=${container_name} \ --device=/dev/davinci0:/dev/davinci0 \ --device=/dev/davinci_manager \ --device=/dev/hisi_hdc:/dev/hisi_hdc \ -v /usr/local/sbin:/usr/local/sbin \ -v /usr/local/Ascend/driver:/usr/local/Ascend/driver \ -v /dev/shm:/dev/shm \ -v /opt/enpu/vcann-rt/lib/libvruntime.so:/opt/enpu/vcann-rt/lib/libvruntime.so \ -v /opt/enpu/vcann-rt/tools/enpu-monitor:/opt/enpu/vcann-rt/tools/enpu-monitor \ -v /xxx/npu_info.config:/etc/enpu/vcann-rt/npu_info.config \ -v ${preload_path}/ld.so.preload:/etc/ld.so.preload \ ${image_name} /bin/bash如果遇到容器启动报错
libboundscheck.so: cannot open shared object file: No such file or directory或者GLIBC_xxx not found。解决办法:-
取消容器启动命令中ld.so.preload文件的映射,并重新启动容器。
-
参照文末FAQ中对应的步骤执行。
-
执行
export LD_PRELOAD=/opt/enpu/vcann-rt/lib/libvruntime.so命令加载动态库。
文件/工具 路径 软切分动态库 主机侧和容器内均为建议路径: /opt/enpu/vcann-rt/lib/libvruntime.so监测工具 主机侧和容器内均为建议路径: /opt/enpu/vcann-rt/tools/enpu-monitor物理NPU设备 主机侧和容器内均为固定路径: /dev/davinci0共享内存设备 主机侧和容器内均为固定路径: /dev/shm配置文件 - 主机侧可存放在自定义路径。
- 容器内为固定路径:
/etc/enpu/vcann-rt/npu_info.config
预加载动态库文件 主机侧可存放在自定义路径,容器内为固定路径: /etc/ld.so.preload。不建议将ld.so.preload文件放置在主机的/etc目录,否则将在主机侧预加载软切分动态库,可能影响主机侧业务。 -
启动业务,使用vCANN-RT服务
-
拉起容器之后,可通过环境变量
ENPU_LOG_LEVEL配置日志级别。日志级别由高到底分别是FATAL(0), ERROR(1), WARN(2), INFO(3), DEBUG(4)。默认日志级别为INFO。例如:export ENPU_LOG_LEVEL=3 -
推理任务启动时,会自动拉起vCANN-RT服务进行算力控制和显存控制,软切分服务启动成功之后会设置一个进程级环境变量
ENPU_ENABLE=True。如果日志回显内容为"Successfully to initialize all module.", 则表示vCANN-RT服务启动成功。 -
在容器内可查询配置文件获取vNPU资源配额等信息:
cat /etc/enpu/vcann-rt/npu_info.config -
在容器内可通过监测工具查询vNPU资源配额和内存使用情况等信息。
/opt/enpu/vcann-rt/tools/enpu-monitor
环境变量汇总
表 10 环境变量列表
| 环境变量 | 范围 | 默认值 | 说明 |
|---|---|---|---|
ASCEND_HOME_PATH |
编译 | /usr/local/Ascend/cann |
CANN安装路径,可通过 source /path/to/cann/set_env.sh 设置。 |
ENPU_ASCEND_DRIVER_PATH |
编译 | /usr/local/Ascend |
HDK driver安装路径。 |
ENPU_LOG_LEVEL |
运行 | 3 |
日志级别,FATAL(0), ERROR(1), WARN(2), INFO(3), DEBUG(4)。 |
ENPU_ENABLE |
运行 | True |
vCANN-RT启动成功之后设置的进程级环境变量。 |
约束
- 由于vCANN-RT解决方案使用了共享内存,因此用户需要确保在可信用户范围内使用。
- 由于硬件设备的限制(可以参考昇腾社区使用约束),建议vCANN-RT最大切分数量不超过单个device支持的最大用户进程数。
- 用户对aicore算力资源的配额设置不能超过100%,对HBM显存资源的配额设置不能超过当前物理卡的可用显存资源总量,否则可能在容器拉起或者业务运行时出现异常。例如:HBM配额设置超限之后,可能会在业务运行时出现OOM异常报错。
- 采用docker方式部署时,用户需要保证同一个device设置为同一种调度策略,否则可能会导致业务运行异常。
- 若用户通过源码自行编译软切分动态库,则需要保证主机侧的CANN版本和容器镜像中的CANN版本保持一致。
- 建议通过k8s的部署方式使用软切分能力,由MindCluster统一提供资源调度和管理能力。
- 若不具备k8s部署条件,可以通过docker部署方式使用软切分能力,但需要管理员统一规划docker容器aicore和HBM的配置总和不超过资源总量,且单device仅允许配置单种调度策略。如若未按照上述约束配置,可能出现以下业务影响:
- 若多容器配置的aicore总量超出100%, 各容器无法按照原先设定的资源使用。
- 若多容器配置的HBM总量超出当前单Device可用内存大小,某一容器内的业务运行时,可能因为实际内存不足,导致报错OOM内存不足。
- 若单卡上各容器配置的调度策略不相同,各容器无法按照原先设定的资源使用,建议各容器配置的调度策略相同。
- 当前vCANN-RT方案适配CANN软件版本为商发版本9.1.0,由于版本限制,暂时不支持persistent task的使用。
FAQ
-
hook拦截runtime API提示
can't find function:当前vCANN-RT方案适配CANN软件版本为商发版本9.1.0,部分runtime API在CANN其他版本未支持。
-
容器启动或者业务运行时报错
GLIBC_xxx not found:由于GLIBC兼容性问题,运行环境和编译环境的GLIBC版本不兼容,建议运行环境的版本大于等于编译环境,或者在容器内编译。
-
容器启动或者业务运行时报错
libboundscheck.so: cannot open shared object file: No such file or directory:由于软切分动态库在运行时依赖安全函数库,因此用户需要确保容器中存在安全函数库并能够被正常链接,可以参考 https://gitcode.com/openeuler/libboundscheck 完成构建部署。
-
容器内模型业务运行时报错
[SqCqManage] Alloc sq cq fail......:由于模型在推理框架中运行时,推理框架使用的stream流资源过多,超出了NPU硬件资源的限制导致报错。