【NPU】Ascend Docker Runtime v26.0.1 之三 runtime/dcmi/ — 超深度逐行分析
runtime/dcmi/ — 超深度逐行分析
三个文件:dcmi.go (360行) + dcmi_api.go (101行) + dcManager.go (294行) = 755行
核心职责:NPU设备管理接口封装,通过CGO调用libdcmi.so
一、模块定位
1.1 业务职责
runtime/dcmi/ 是设备管理抽象层,负责:
- 驱动版本自适应 — 自动检测V1/V2驱动,选择匹配的Worker
- 设备查询 — 获取卡列表、设备列表、芯片信息、产品类型
- 虚拟设备管理 — 创建/销毁vNPU(虚拟NPU切分)
- 物理ID↔逻辑ID转换 — 将用户指定的物理设备ID转换为驱动内部逻辑ID
- CGO桥接 — 封装所有对libdcmi.so的C语言调用
1.2 在系统中的位置
二、dcmi_api.go 逐行解析(101行)
2.1 数据结构
type VDeviceInfo struct {
CardID int32
DeviceID int32
VdeviceID int32
}
| 字段 | 含义 | 示例 |
|---|---|---|
| CardID | NPU卡ID(物理插槽) | 0 |
| DeviceID | 卡内设备ID | 1 |
| VdeviceID | 虚拟设备ID(切分后生成) | 42 |
2.2 WorkerInterface 接口
type WorkerInterface interface {
Initialize() error
ShutDown()
CreateVDevice(uniqueID int32, coreNum string) (VDeviceInfo, error)
DestroyVDevice(uniqueID int32, vDevID int32) error
GetProductType() (string, error)
GetChipName() (string, error)
}
6个方法的职责:
| 方法 | 输入 | 输出 | 用途 |
|---|---|---|---|
Initialize |
无 | error | 初始化dcmi库连接 |
ShutDown |
无 | 无 | 关闭dcmi库连接 |
CreateVDevice |
uniqueID(物理设备ID), coreNum(切分模板) | VDeviceInfo, error | 创建虚拟NPU |
DestroyVDevice |
uniqueID, vDevID(虚拟设备ID) | error | 销毁虚拟NPU |
GetProductType |
无 | string, error | 获取产品型号 |
GetChipName |
无 | string, error | 获取芯片名称 |
设计意图:策略模式接口,V1和V2驱动各自实现此接口,调用方无需关心驱动版本。
2.3 CreateVDevice 顶层API
func CreateVDevice(w WorkerInterface, spec *specs.Spec, devices []int) (VDeviceInfo, error) {
invalidVDevice := VDeviceInfo{CardID: -1, DeviceID: -1, VdeviceID: -1}
if spec == nil {
return invalidVDevice, fmt.Errorf("spec is nil")
}
splitDevice, err := extractVpuParam(spec)
if err != nil {
return invalidVDevice, err
}
if splitDevice == "" {
return invalidVDevice, nil
}
if len(devices) != 1 || devices[0] < 0 || devices[0] >= hiAIMaxCardNum*hiAIMaxDeviceNum {
hwlog.RunLog.Errorf("invalid devices: %v", devices)
return invalidVDevice, fmt.Errorf("invalid devices: %v", devices)
}
if err := w.Initialize(); err != nil {
return invalidVDevice, fmt.Errorf("cannot init dcmi : %v", err)
}
defer w.ShutDown()
vDeviceInfo, err := w.CreateVDevice(int32(devices[0]), splitDevice)
if err != nil || vDeviceInfo.VdeviceID < 0 {
hwlog.RunLog.Errorf("cannot create vd or vdevice is wrong: %v %v", vDeviceInfo.VdeviceID, err)
return invalidVDevice, err
}
return vDeviceInfo, nil
}
逐行解析:
- 定义无效返回值
invalidVDevice(所有ID为-1) - nil检查保护
extractVpuParam— 从OCI Spec环境变量中提取vNPU切分模板- 如果splitDevice为空 → 不是虚拟模式,返回无效值但无错误
- 设备数校验:vNPU切分只允许1个物理设备(
len(devices) != 1),且ID在有效范围内 - 初始化Worker → 创建虚拟设备 → defer关闭
- 如果创建失败或VdeviceID<0 → 返回错误
设计意图:此函数是对Worker.CreateVDevice的包装,增加了Spec解析和参数校验。
2.4 extractVpuParam — vNPU切分模板解析
func extractVpuParam(spec *specs.Spec) (string, error) {
allowSplit := map[string]string{
"vir01": "vir01", "vir02": "vir02", "vir04": "vir04", "vir08": "vir08", "vir16": "vir16",
"vir02_1c": "vir02_1c", "vir03_1c_8g": "vir03_1c_8g", "vir04_3c": "vir04_3c",
"vir04_4c_dvpp": "vir04_4c_dvpp", "vir04_3c_ndvpp": "vir04_3c_ndvpp",
"vir05_1c_8g": "vir05_1c_8g", "vir05_1c_16g": "vir05_1c_16g",
"vir06_1c_16g": "vir06_1c_16g", "vir10_3c_16g": "vir10_3c_16g",
"vir10_3c_16g_nm": "vir10_3c_16g_nm", "vir10_3c_32g": "vir10_3c_32g",
"vir10_4c_16g_m": "vir10_4c_16g_m", "vir12_3c_32g": "vir12_3c_32g",
}
for _, line := range spec.Process.Env {
words := strings.Split(line, "=")
const LENGTH int = 2
if len(words) != LENGTH {
continue
}
if strings.TrimSpace(words[0]) == api.AscendVnpuSpescEnv {
if split, ok := allowSplit[words[1]]; ok && split != "" {
return split, nil
}
return "", fmt.Errorf("cannot parse param : %v", words[1])
}
}
return "", nil
}
vNPU切分模板说明:
| 模板名 | 含义 |
|---|---|
vir01 |
1/16切分(最小) |
vir02 |
1/8切分 |
vir04 |
1/4切分 |
vir08 |
1/2切分 |
vir16 |
不切分(全卡) |
vir02_1c |
1/8切分+1核 |
vir04_3c |
1/4切分+3核 |
vir04_4c_dvpp |
1/4切分+4核+DVPP |
vir04_3c_ndvpp |
1/4切分+3核+无DVPP |
vir05_1c_8g |
1/3.2切分+1核+8GB |
vir10_3c_16g |
1/1.6切分+3核+16GB |
vir10_4c_16g_m |
1/1.6切分+4核+16GB+内存增强 |
vir12_3c_32g |
1/1.3切分+3核+32GB |
逐行解析:
- 定义允许的切分模板白名单(map)
- 遍历容器环境变量
- 找到
ASCEND_VNPU_SPECS环境变量 - 如果值在白名单中 → 返回切分模板
- 如果值不在白名单 → 返回错误(防止非法切分参数)
- 如果环境变量不存在 → 返回空字符串(非虚拟模式)
设计意图:严格的白名单校验,防止用户传入非法切分参数导致驱动异常。
三、dcmi.go 逐行解析(360行)
3.1 常量定义
const (
retError = -1
hiAIMaxCardNum = 64 // 最大卡数
hiAIMaxCardID = math.MaxInt32 // 卡ID上限
hiAIMaxDeviceID = math.MaxInt32 // 设备ID上限
hiAIMaxDeviceNum = 4 // 每卡最大设备数
maxChipNameLen = 32 // 芯片名最大长度
productTypeLen = 64 // 产品类型最大长度
notSupportCode = -8255 // "不支持"错误码
coreNumLen = 32 // 切分模板名最大长度
vfgID = 4294967295 // 虚拟组ID默认值(0xFFFFFFFF)
notSupportString = "[not support]"
)
| 常量 | 值 | 用途 |
|---|---|---|
hiAIMaxCardNum |
64 | 系统最多支持64张NPU卡 |
hiAIMaxDeviceNum |
4 | 每张卡最多4个芯片 |
maxChipNameLen |
32 | C结构体中芯片名字段长度 |
vfgID |
0xFFFFFFFF | 虚拟功能组ID,默认自动分配 |
notSupportCode |
-8255 | DCMI库返回的"不支持"错误码 |
3.2 managerList — Worker注册表
var managerList = []WorkerInterface{
&NpuV1Worker{DcMgr: &DcV1Manager{}},
&NpuV2Worker{DcMgr: &DcV2Manager{}},
}
设计意图:全局Worker列表,V1在前V2在后。GetMatchingNpuWorker 按顺序尝试初始化,第一个成功的即为匹配的驱动版本。
3.3 ChipInfo 结构体
type ChipInfo struct {
Type string `json:"chip_type"`
Name string `json:"chip_name"`
Version string `json:"chip_version"`
}
- 从C结构体
DcmiChipInfo转换而来 Name用于判断芯片类型(如"Ascend910A"→Ascend910)
3.4 驱动接口定义
type DcDriverV1Interface interface {
DcInitialize() error
DcShutDown()
DcGetCardList() (int32, []int32, error)
DcGetDeviceNumInCard(cardID int32) (int32, error)
DcGetDeviceLogicID(cardID, deviceID int32) (int32, error)
DcGetProductType(cardID, deviceID int32) (string, error)
DcCreateVDevice(cardID, deviceID int32, coreNum string) (int32, error)
DcDestroyVDevice(cardID, deviceID int32, vDevID int32) error
DcGetChipInfo(cardID, deviceID int32) (*ChipInfo, error)
DcGetDeviceLogicidFromPhyid(phyID int32) (int32, error)
}
type DcDriverV2Interface interface {
DcInitialize() error
DcShutDown()
DcCreateVDevice(deviceID int32, coreNum string) (int32, error)
DcDestroyVDevice(deviceID int32, vDevID int32) error
DcGetChipInfo(deviceID int32) (*ChipInfo, error)
DcGetDeviceList() (int32, []int32, error)
}
V1 vs V2 接口差异:
| 差异点 | V1 | V2 |
|---|---|---|
| 设备寻址 | cardID + deviceID 二级寻址 | deviceID 一级寻址 |
| 卡列表 | DcGetCardList → 每卡DcGetDeviceNumInCard | DcGetDeviceList 直接获取所有设备 |
| 产品类型 | 支持 DcGetProductType | 不支持(返回"[not support]") |
| 物理ID转换 | DcGetDeviceLogicidFromPhyid | 不需要(直接用deviceID) |
| 虚拟设备 | 支持 | 不支持(返回错误) |
3.5 GetMatchingNpuWorker — 工厂函数
func GetMatchingNpuWorker() (WorkerInterface, error) {
for _, manager := range managerList {
err := manager.Initialize()
if err == nil {
manager.ShutDown()
hwlog.RunLog.Infof("worker type: %T", manager)
return manager, nil
}
}
return nil, fmt.Errorf("failed to find a valid manager")
}
逐行解析:
- 遍历managerList:先尝试V1,再尝试V2
- 调用
Initialize()尝试初始化dcmi库 - 如果成功 → 立即
ShutDown()(因为只是探测,实际使用时会重新初始化) - 返回匹配的Worker
- 如果都失败 → 返回错误
设计意图:V1调用 DcmiInit(),V2调用 DcmiV2Init()。在新版硬件上调用V1初始化会失败,反之亦然。利用这个特性来自动检测驱动版本。
3.6 NpuV1Worker.FindDevice — 物理ID到卡/设备映射
func (w *NpuV1Worker) FindDevice(visibleDevice int32) (int32, int32, error) {
targetLogicID, err := w.DcMgr.DcGetDeviceLogicidFromPhyid(visibleDevice)
if err != nil {
return 0, 0, fmt.Errorf("cannot convert phy id to logic id, err: %v", err)
}
_, cardList, err := w.DcMgr.DcGetCardList()
if err != nil {
return 0, 0, fmt.Errorf("get card list err : %v", err)
}
targetDeviceID, targetCardID := int32(math.MaxInt32), int32(math.MaxInt32)
for _, cardID := range cardList {
deviceCount, err := w.DcMgr.DcGetDeviceNumInCard(cardID)
if err != nil {
return 0, 0, fmt.Errorf("cannot get device num in card : %v", err)
}
for deviceID := int32(0); deviceID < deviceCount; deviceID++ {
logicID, err := w.DcMgr.DcGetDeviceLogicID(cardID, deviceID)
if err != nil {
return 0, 0, fmt.Errorf("cannot get logic id : %v", err)
}
if logicID == targetLogicID {
targetCardID, targetDeviceID = cardID, deviceID
}
}
}
return targetDeviceID, targetCardID, nil
}
逐行解析:
- 将物理ID(用户指定的
visibleDevice)转换为逻辑ID - 获取所有卡的列表
- 双层遍历:遍历每张卡,遍历卡内每个设备
- 对每个设备获取其逻辑ID
- 如果逻辑ID匹配目标 → 记录cardID和deviceID
- 返回找到的deviceID和cardID
设计意图:V1驱动使用"物理ID"作为用户接口(如ASCEND_VISIBLE_DEVICES=0),但内部操作需要cardID+deviceID二维坐标。此函数完成这个映射。
3.7 NpuV1Worker.GetChipName
func (w *NpuV1Worker) GetChipName() (string, error) {
invalidName := ""
if err := w.Initialize(); err != nil {
return invalidName, fmt.Errorf("cannot init dcmi : %v", err)
}
defer w.ShutDown()
cardNum, cardList, err := w.DcMgr.DcGetCardList()
if err != nil {
return invalidName, err
}
if cardNum == 0 {
return invalidName, fmt.Errorf("get chip info failed, no card found")
}
for _, cardID := range cardList {
devNum, err := w.DcMgr.DcGetDeviceNumInCard(cardID)
if err != nil || devNum == 0 {
continue
}
for devID := int32(0); devID < devNum; devID++ {
chipInfo, err := w.DcMgr.DcGetChipInfo(cardID, devID)
if err != nil {
continue
}
if !isValidChipInfo(chipInfo) {
continue
}
return (*chipInfo).Name, nil
}
}
return invalidName, fmt.Errorf("cannot get valid chip info")
}
执行流程:
- 初始化dcmi → 获取卡列表 → 遍历卡 → 遍历设备 → 获取芯片信息 → 返回第一个有效芯片名
- 容错策略:任何单步失败都
continue跳过,继续尝试下一个设备 - defer确保关闭dcmi连接
设计意图:只需要获取任意一个有效芯片的名称即可确定整个系统的芯片类型(假设同一机器上芯片类型一致)。
3.8 NpuV2Worker.GetChipName
func (a *NpuV2Worker) GetChipName() (string, error) {
invalidName := ""
if err := a.Initialize(); err != nil {
return invalidName, fmt.Errorf("cannot init dcmi : %v", err)
}
defer a.ShutDown()
deviceNum, deviceList, err := a.DcMgr.DcGetDeviceList()
// ...
for _, devID := range deviceList {
chipInfo, err := a.DcMgr.DcGetChipInfo(devID)
// ...
return (*chipInfo).Name, nil
}
return invalidName, fmt.Errorf("cannot get valid chip info")
}
与V1的区别:
- V1:卡列表→每卡设备数→遍历cardID+deviceID
- V2:直接获取设备列表→遍历deviceID
3.9 NpuV1Worker.CreateVDevice
func (w *NpuV1Worker) CreateVDevice(uniqueID int32, coreNum string) (VDeviceInfo, error) {
deviceID, cardID, err := w.FindDevice(uniqueID)
if err != nil {
return VDeviceInfo{CardID: -1, DeviceID: -1, VdeviceID: -1}, err
}
vdevId, err := w.DcMgr.DcCreateVDevice(cardID, deviceID, coreNum)
if err != nil {
return VDeviceInfo{CardID: -1, DeviceID: -1, VdeviceID: math.MaxInt32}, err
}
if vdevId > math.MaxInt32 {
return VDeviceInfo{CardID: cardID, DeviceID: deviceID, VdeviceID: math.MaxInt32},
fmt.Errorf("create virtual device failed, vdeviceId too large")
}
return VDeviceInfo{CardID: cardID, DeviceID: deviceID, VdeviceID: int32(vdevId)}, nil
}
逐行解析:
FindDevice— 将物理ID转换为cardID+deviceIDDcCreateVDevice— 调用CGO创建虚拟设备- 溢出检查:
vdevId > math.MaxInt32 - 返回完整的VDeviceInfo
3.10 NpuV2Worker.CreateVDevice
func (a *NpuV2Worker) CreateVDevice(uniqueID int32, coreNum string) (VDeviceInfo, error) {
vdevId, err := a.DcMgr.DcCreateVDevice(uniqueID, coreNum)
if err != nil {
return VDeviceInfo{
CardID: -1,
DeviceID: -1,
VdeviceID: -1,
}, err
}
return VDeviceInfo{VdeviceID: vdevId}, nil
}
注意:V2的 DcCreateVDevice 实际返回错误(“ascend 950 not support virtual device”),所以这段代码在当前版本实际上不会成功执行。
四、dcManager.go 逐行解析(294行)
4.1 CGO导入
// #cgo LDFLAGS: -ldl
// #include "dcmi_interface_api.h"
import "C"
LDFLAGS: -ldl— 链接动态加载库(dlopen)#include "dcmi_interface_api.h"— DCMI C接口头文件import "C"— Go的CGO机制,允许调用C函数
4.2 DcV1Manager.DcInitialize
func (d *DcV1Manager) DcInitialize() error {
cDlPath := C.CString(string(make([]byte, int32(C.PATH_MAX))))
defer C.free(unsafe.Pointer(cDlPath))
if err := C.DcmiInitDl(cDlPath); err != C.SUCCESS {
errInfo := fmt.Errorf("dcmi lib load failed, error code: %d", int32(err))
return errInfo
}
dlPath := C.GoString(cDlPath)
if _, err := mindxcheckutils.RealFileChecker(dlPath, true, false, mindxcheckutils.DefaultSize); err != nil {
return err
}
if err := C.DcmiInit(); err != C.SUCCESS {
errInfo := fmt.Errorf("dcmi init failed, error code: %d", int32(err))
return errInfo
}
return nil
}
逐行解析:
make([]byte, C.PATH_MAX)— 创建PATH_MAX长度的空字节切片C.CString— 转换为C字符串(用于接收dcmi库路径输出)C.DcmiInitDl(cDlPath)— 动态加载libdcmi.so(dlopen方式),路径通过cDlPath返回defer C.free— 释放C字符串内存(防内存泄漏)C.GoString(cDlPath)— 将C字符串转回Go字符串RealFileChecker— 安全校验加载的dcmi库文件C.DcmiInit()— 初始化dcmi设备管理连接
设计意图:两步初始化——先加载动态库(dlopen),再初始化设备连接。安全校验确保加载的是合法dcmi库。
4.3 DcV1Manager.DcGetCardList
func (d *DcV1Manager) DcGetCardList() (int32, []int32, error) {
var ids [hiAIMaxCardNum]C.int
var cNum C.int
if err := C.DcmiGetCardNumList(&cNum, &ids[0], hiAIMaxCardNum); err != 0 {
return retError, nil, fmt.Errorf("get card list failed, error code: %d", int32(err))
}
if cNum <= 0 || cNum > hiAIMaxCardNum {
return retError, nil, fmt.Errorf("get error card quantity: %d", int32(cNum))
}
var cardNum = int32(cNum)
var cardIDList []int32
for i := int32(0); i < cardNum; i++ {
cardID := int32(ids[i])
if cardID < 0 {
continue
}
cardIDList = append(cardIDList, cardID)
}
return cardNum, cardIDList, nil
}
逐行解析:
- 声明C数组
ids[64]和数量cNum C.DcmiGetCardNumList— C函数填充卡数量和卡ID列表- 校验数量:1 ≤ cNum ≤ 64
- 遍历C数组,过滤负值,转为Go的[]int32
4.4 DcV1Manager.DcCreateVDevice
func (d *DcV1Manager) DcCreateVDevice(cardID, deviceID int32, coreNum string) (int32, error) {
var createInfo C.struct_DcmiCreateVdevOut
createInfo.vdevId = C.uint(math.MaxUint32)
var deviceCreateStr C.struct_DcmiCreateVdevResStru
deviceCreateStr = C.struct_DcmiCreateVdevResStru{
vdevId: C.uint(vfgID),
vfgId: C.uint(vfgID),
}
deviceCreateStrArr := [coreNumLen]C.char{0}
for i := 0; i < len(coreNum); i++ {
if i >= coreNumLen {
return math.MaxInt32, fmt.Errorf("wrong template")
}
deviceCreateStrArr[i] = C.char(coreNum[i])
}
deviceCreateStr.templateName = deviceCreateStrArr
ret := C.DcmiCreateVdevice(C.int(cardID), C.int(deviceID), &deviceCreateStr, &createInfo)
if ret != 0 {
return math.MaxInt32, fmt.Errorf("create virtual device failed, error code: %d", int32(ret))
}
return int32(createInfo.vdevId), nil
}
逐行解析:
- 创建输出结构体
DcmiCreateVdevOut,初始vdevId设为MaxUint32(表示未创建) - 创建输入结构体
DcmiCreateVdevResStru,设置vdevId和vfgId为默认值(0xFFFFFFFF) - 将切分模板名(如"vir04")逐字节复制到C字符数组
- 模板名长度校验(≤32)
- 调用
C.DcmiCreateVdevice— CGO调用创建虚拟设备 - 返回创建的vdevId
关键数据结构:
4.5 DcV1Manager.DcGetChipInfo
func (d *DcV1Manager) DcGetChipInfo(cardID, deviceID int32) (*ChipInfo, error) {
if !isValidCardIDAndDeviceID(cardID, deviceID) {
return nil, fmt.Errorf("cardID(%d) or deviceID(%d) is invalid", cardID, deviceID)
}
var chipInfo C.struct_DcmiChipInfo
if rCode := C.DcmiGetDeviceChipInfo(C.int(cardID), C.int(deviceID), &chipInfo); int32(rCode) != 0 {
return nil, fmt.Errorf("get device ChipInfo information failed, ...")
}
name := convertUCharToCharArr(chipInfo.chipName)
cType := convertUCharToCharArr(chipInfo.chipType)
ver := convertUCharToCharArr(chipInfo.chipVer)
chip := &ChipInfo{
Name: string(name),
Type: string(cType),
Version: string(ver),
}
if !isValidChipInfo(chip) {
return nil, fmt.Errorf("get device ChipInfo information failed, chip info is empty, ...")
}
return chip, nil
}
逐行解析:
- 参数校验(cardID和deviceID范围检查)
- 声明C结构体
DcmiChipInfo - 调用
C.DcmiGetDeviceChipInfo获取芯片信息 convertUCharToCharArr— 将C的uchar数组转为Go byte切片(遇0截断)- 构造Go的ChipInfo结构体
- 有效性校验
4.6 convertUCharToCharArr — C数组转Go
func convertUCharToCharArr(cgoArr [maxChipNameLen]C.uchar) []byte {
var charArr []byte
for _, v := range cgoArr {
if v == 0 {
break
}
charArr = append(charArr, byte(v))
}
return charArr
}
- C字符串以null(0)结尾,遍历到0即停止
- 将C的
[32]uchar转为Go的[]byte,再转为string
4.7 DcV2Manager — V2 CGO实现
func (d *DcV2Manager) DcInitialize() error {
// ...同V1的DcmiInitDl...
if err := C.DcmiV2Init(); err != C.SUCCESS {
return fmt.Errorf("dcmi init failed, error code: %d", int32(err))
}
return nil
}
关键差异:V2调用 C.DcmiV2Init() 而非 C.DcmiInit()。
func (d *DcV2Manager) DcCreateVDevice(deviceID int32, coreNum string) (int32, error) {
return -1, fmt.Errorf("create error, ascend 950 not support virtual device")
}
func (d *DcV2Manager) DcDestroyVDevice(deviceID int32, vDevID int32) error {
return fmt.Errorf("destroy error, ascend 950 not support virtual device")
}
V2不支持虚拟设备:Ascend 950(910A5)不支持vNPU切分,直接返回错误。
func (d *DcV2Manager) DcGetDeviceList() (int32, []int32, error) {
var ids [hiAIMaxCardNum]C.int
var cNum C.int
if err := C.DcmiV2GetDeviceList(&ids[0], &cNum, hiAIMaxCardNum); err != 0 {
return retError, nil, fmt.Errorf("get device list failed, error code: %d", int32(err))
}
// ...校验+转换...
return deviceNum, deviceIDList, nil
}
V2直接获取设备列表,无需先获取卡列表再遍历卡内设备。
五、V1 vs V2 完整对比
| 对比维度 | V1 (DcV1Manager) | V2 (DcV2Manager) |
|---|---|---|
| 初始化 | DcmiInitDl + DcmiInit | DcmiInitDl + DcmiV2Init |
| 设备寻址 | cardID + deviceID 二级 | deviceID 一级 |
| 获取设备列表 | DcmiGetCardNumList → 遍历卡 → DcmiGetDeviceNumInCard | DcmiV2GetDeviceList 一步到位 |
| 芯片信息 | DcmiGetDeviceChipInfo(cardID, deviceID) | DcmiV2GetDeviceChipInfo(deviceID) |
| 产品类型 | 支持 DcmiGetProductType | 不支持(返回"[not support]") |
| vNPU切分 | 支持 DcmiCreateVdevice | 不支持(返回错误) |
| 物理ID转换 | 需要 DcmiGetDeviceLogicidFromPhyid | 不需要 |
| 适用芯片 | Ascend 910, 310, 310P, 310B | Ascend 910B, A5 (950) |
鲲鹏昇腾开发者社区是面向全社会开放的“联接全球计算开发者,聚合华为+生态”的社区,内容涵盖鲲鹏、昇腾资源,帮助开发者快速获取所需的知识、经验、软件、工具、算力,支撑开发者易学、好用、成功,成为核心开发者。
更多推荐


所有评论(0)