Ascend vNPU 切片如何工作
与 NVIDIA vGPU 不同——后者可以请求任意内存大小——Huawei NPU 虚拟化是基于模板的。每种芯片型号都有一组固定的切片大小(称为 templates),并且固件只接受这些大小;插件和调度器始终会将内存请求向上取整到最接近的模板。
有关设置 Ascend device plugin 以及下面引用的 hami-scheduler-device ConfigMap 的安装步骤,请参见 Install for Huawei Ascend NPU。
每种芯片型号对应的三个数值
hami-scheduler-device ConfigMap 中的 vnpus 部分为每种芯片型号定义了插件和调度器所需的三个值:
例如,Ascend910B4 的默认条目如下:
节点上显示的内容
对于每张物理卡,插件都会向 kubelet 广告 memoryAllocatable / smallestTemplateMemory 个“槽位”。每个槽位代表该卡上的一个潜在切片;切片的实际大小会在调度时根据请求的内存再决定。
示例:一台使用上面默认配置的节点,包含 8 × Ascend 910B4(每张 32 GiB):
-
最小模板是
vir05_1c_8g(8 GiB),因此每张卡暴露32768 / 8192 = 4个槽位。 -
节点的
status.allocatable只包含槽位数量:
对应的 huawei.com/Ascend910B4-memory 不是 kubelet 扩展资源,因此不会出现在节点上。插件会改为在 hami.io/node-register-Ascend910B4 注解中发布每张卡的 UUID、总内存和 aiCore;hami-scheduler 读取该注解,以维护按卡划分的内存预算。一个 Pod 会同时消耗 status.allocatable 中的一个槽位以及调度器跟踪的内存预算中的一部分。
你可以使用以下命令查看调度器侧视图:
内存请求如何取整 — trimMemory
当 Pod 被准入时,HAMi webhook 会从最小模板到最大模板依次遍历,并选择第一个 memory ≥ 请求内存的模板。该模板定义了切片实际使用的内存和 aiCore 数量。
随后,webhook 会将 Pod 的 huawei.com/Ascend910B4-memory 请求重写为取整后的值,因此你在准入后的 Pod spec 中看到的是切片大小,而不是你最初请求的数值。
详细过程:8 × 910B4,同一节点上的两个 Pod
初始状态——每张卡都是空闲的:剩余 32 GiB,剩余 20 aiCore。
第三个请求 1 GiB 的 Pod(会取整为 8 GiB / 5 aiCore)仍然可以刚好放入 #0 卡并将其填满;第四个则会迫使调度器切换到 #1 卡。若使用 gpuSchedulerPolicy=spread,则每个新的切片都会从一张新的卡开始。
当与切片绑定的容器启动后,Ascend Docker Runtime 会读取插件注入的环境变量:
然后请求固件创建实际的 vNPU。当 Pod 终止且 vNPU 处于空闲状态时,插件的周期性清理(CleanupIdleVNPUs)会在下一次 tick 时将其销毁,从而使该槽位可以再次被复用。
需要注意的事项
- 没有小数模板。 即使在最小模板为 8 GiB 的芯片上请求 2 GiB,仍然会占用 8 GiB 和 5 aiCore。请选择与你的典型工作负载相匹配的芯片 / 模板集合。
- 切片仅限单卡。
huawei.com/Ascend910B4 > 1会被解释为“我要 N 张整卡”——如果请求同时包含大于 1 的数量和小于memoryAllocatable的内存请求,webhook 会拒绝。 - ConfigMap 是唯一事实来源。 如果你的卡使用的内存大小不是默认值,或者你需要不同的模板形状,请编辑
hami-scheduler-device中该芯片对应的vnpus条目,并重启hami-ascend-device-pluginPods。下一次 chart 升级时,这些修改会丢失。