• Русский
  • Как работает нарезка Ascend vNPU

    В отличие от NVIDIA vGPU, где можно запросить любой размер памяти, виртуализация Huawei NPU основана на шаблонах. Для каждой модели чипа задан фиксированный набор размеров срезов (так называемых templates), которые принимает прошивка; плагин и scheduler всегда округляют запрос памяти вверх до ближайшего шаблона.

    С установочными шагами, которые настраивают Ascend device plugin и ConfigMap hami-scheduler-device, упомянутый ниже, см. Установка для Huawei Ascend NPU.

    Три числа для каждой модели чипа

    Раздел vnpus в ConfigMap hami-scheduler-device определяет для каждой модели чипа три вещи, которые нужны плагину и scheduler:

    FieldMeaning
    memoryAllocatableПамять, которую одна физическая карта предоставляет для нарезки (в MiB).
    memoryCapacityАппаратный верхний предел. Запрос больше memoryAllocatable, но ≤ memoryCapacity, занимает всю карту целиком.
    templates[]Допустимые размеры срезов (memory, aiCore, необязательный aiCPU). При запуске плагин сортирует их по memory по возрастанию.

    Например, запись по умолчанию для Ascend910B4 выглядит так:

    - chipName: 910B4
      commonWord: Ascend910B4
      resourceName: huawei.com/Ascend910B4
      resourceMemoryName: huawei.com/Ascend910B4-memory
      memoryAllocatable: 32768    # 32 GiB per card
      memoryCapacity: 32768
      aiCore: 20
      aiCPU: 7
      templates:
        - name: vir05_1c_8g        # 8 GiB slice, 5 aiCore, 1 aiCPU
          memory: 8192
          aiCore: 5
          aiCPU: 1
        - name: vir10_3c_16g       # 16 GiB slice, 10 aiCore, 3 aiCPU
          memory: 16384
          aiCore: 10
          aiCPU: 3

    Что видно на node

    Для каждой физической карты плагин объявляет kubelet memoryAllocatable / smallestTemplateMemory «слотов». Каждый слот представляет один потенциальный срез на этой карте; фактический размер среза определяется позже, во время scheduling, по запрошенной памяти.

    Пример — node с 8 × Ascend 910B4 (по 32 GiB каждая), использующий конфигурацию по умолчанию выше:

    • Самый маленький шаблон — vir05_1c_8g (8 GiB), поэтому каждая карта предоставляет 32768 / 8192 = 4 слота.

    • status.allocatable node содержит только количество слотов:

      status.allocatable:
        huawei.com/Ascend910B4: 32   # 8 cards × 4 slots

    Связанный ресурс huawei.com/Ascend910B4-memory не является расширенным ресурсом kubelet и не отображается на node. Вместо этого плагин публикует UUID каждой карты, общий объем памяти и aiCore в аннотации hami.io/node-register-Ascend910B4; hami-scheduler читает эту аннотацию, чтобы вести бюджет памяти для каждой карты отдельно. Pod одновременно потребляет один слот из status.allocatable и часть бюджета памяти, отслеживаемого scheduler.

    Посмотреть представление со стороны scheduler можно так:

    kubectl get node {ascend-node} -o jsonpath='{.metadata.annotations.hami\.io/node-register-Ascend910B4}'

    Как округляется запрос памяти — trimMemory

    Когда pod допускается к запуску, HAMi webhook проходит по шаблонам от меньшего к большему и выбирает первый шаблон, у которого memory запрошенной памяти. Этот шаблон определяет, сколько памяти и сколько aiCore фактически использует срез.

    templates (sorted): 8192, 16384
    
    requested 2 GiB   -> 8 GiB slice (vir05_1c_8g),  5 aiCore
    requested 6 GiB   -> 8 GiB slice (vir05_1c_8g),  5 aiCore
    requested 10 GiB  -> 16 GiB slice (vir10_3c_16g), 10 aiCore
    requested 20 GiB  -> whole card (32 GiB), all aiCore   # no template fits, but ≤ memoryCapacity
    requested 40 GiB  -> rejected                           # > memoryCapacity

    Затем webhook переписывает запрос huawei.com/Ascend910B4-memory в pod до округленного значения, поэтому после admission в pod spec вы видите размер среза, а не исходно запрошенное значение.

    Пошаговый пример: 8 × 910B4, два pod на одной node

    Начальное состояние — каждая карта пуста: свободно 32 GiB, свободно 20 aiCore.

    StepPod requestsTemplate chosenWhat happens
    1Ascend910B4: 1, Ascend910B4-memory: 2048 (2 GiB)vir05_1c_8g — 8 GiB / 5 aiCoreScheduler binpack-выбирает карту #0; запрос pod переписывается в 8192. Карта #0 теперь: 24 GiB free, 15 aiCore free. Node count drops to 31.
    2Ascend910B4: 1, Ascend910B4-memory: 10240 (10 GiB)vir10_3c_16g — 16 GiB / 10 aiCoreНа карте #0 все еще есть место (24 GiB free, 15 aiCore free) → scheduler повторно использует карту #0. Карта #0 теперь: 8 GiB free, 5 aiCore free. Node count drops to 30.

    Третий pod, запрашивающий 1 GiB (округляется до 8 GiB / 5 aiCore), как раз поместился бы на карту #0 и заполнил бы ее; четвертый заставил бы scheduler перейти на карту #1. При gpuSchedulerPolicy=spread каждый новый срез, наоборот, начинал бы с новой карты.

    Когда контейнеры, привязанные к срезу, запускаются, Ascend Docker Runtime видит переменные окружения, которые внедряет плагин:

    ASCEND_VISIBLE_DEVICES=0          # physical card index
    ASCEND_VNPU_SPECS=vir05_1c_8g     # template name

    и запрашивает у прошивки создание фактического vNPU. Когда pod завершается и vNPU переходит в idle, периодическая очистка плагина (CleanupIdleVNPUs) удаляет его на следующем тике, чтобы слот можно было использовать повторно.

    Что важно помнить

    • Дробных шаблонов нет. Запрос 2 GiB на чипе, у которого самый маленький шаблон — 8 GiB, все равно потребляет 8 GiB и 5 aiCore. Подбирайте chip / набор шаблонов под типичную нагрузку.
    • Нарезка работает только внутри одной карты. huawei.com/Ascend910B4 > 1 интерпретируется как «мне нужно N полных карт» — webhook отклоняет запросы, которые сочетают count > 1 с запросом памяти меньше memoryAllocatable.
    • ConfigMap — источник истины. Если у ваших карт нестандартный размер памяти или вам нужны другие формы шаблонов, отредактируйте запись vnpus для этого chip в hami-scheduler-device и перезапустите pod hami-ascend-device-plugin. Изменения будут потеряны при следующем обновлении chart.