From 77cb497420a0ea09b62559ec3a6602ce9d81f590 Mon Sep 17 00:00:00 2001 From: "chenyu.jia" Date: Wed, 25 Feb 2026 10:35:33 +0800 Subject: [PATCH] Add user manual and usage examples - MANUAL.md: comprehensive installation and API usage guide - examples/: 13 categorized example scripts covering library basics, device info, GPU/memory/VPU monitoring, ECC, topology, NVML compat, etc. Co-authored-by: Cursor Made-with: Cursor Signed-off-by: chenyu.jia --- MANUAL.md | 1020 +++++++++++++++++++++++++ examples/01_library_basics.py | 62 ++ examples/02_device_info.py | 63 ++ examples/03_gpu_monitoring.py | 47 ++ examples/04_memory_monitoring.py | 62 ++ examples/05_vpu_monitoring.py | 68 ++ examples/06_fan_and_power.py | 44 ++ examples/07_ecc_errors.py | 93 +++ examples/08_topology_and_mtlink.py | 143 ++++ examples/09_nvml_compatibility.py | 146 ++++ examples/10_device_paths.py | 48 ++ examples/11_mpc_and_virtualization.py | 97 +++ examples/12_affinity_and_log.py | 70 ++ examples/13_comprehensive_report.py | 118 +++ examples/README.md | 38 + 15 files changed, 2119 insertions(+) create mode 100644 MANUAL.md create mode 100644 examples/01_library_basics.py create mode 100644 examples/02_device_info.py create mode 100644 examples/03_gpu_monitoring.py create mode 100644 examples/04_memory_monitoring.py create mode 100644 examples/05_vpu_monitoring.py create mode 100644 examples/06_fan_and_power.py create mode 100644 examples/07_ecc_errors.py create mode 100644 examples/08_topology_and_mtlink.py create mode 100644 examples/09_nvml_compatibility.py create mode 100644 examples/10_device_paths.py create mode 100644 examples/11_mpc_and_virtualization.py create mode 100644 examples/12_affinity_and_log.py create mode 100644 examples/13_comprehensive_report.py create mode 100644 examples/README.md diff --git a/MANUAL.md b/MANUAL.md new file mode 100644 index 0000000..2e50c44 --- /dev/null +++ b/MANUAL.md @@ -0,0 +1,1020 @@ +# pymtml 安装与使用手册 + +Python Bindings for Moore Threads Management Library (MTML) + +--- + +## 目录 + +- [1. 项目简介](#1-项目简介) +- [2. 环境要求](#2-环境要求) +- [3. 安装方式](#3-安装方式) +- [4. 快速上手](#4-快速上手) +- [5. 原生 MTML API 详细说明](#5-原生-mtml-api-详细说明) +- [6. NVML 兼容层](#6-nvml-兼容层) +- [7. 上下文管理器](#7-上下文管理器) +- [8. 错误处理](#8-错误处理) +- [9. 常量与枚举](#9-常量与枚举) +- [10. 示例程序与运行输出](#10-示例程序与运行输出) +- [11. 测试](#11-测试) +- [12 常见问题](#12-常见问题) + +--- + +## 1. 项目简介 + +**pymtml** 是摩尔线程 GPU 管理库 (MTML) 的 Python 绑定,通过 ctypes 动态加载 `libmtml.so` 共享库,提供对摩尔线程 GPU 设备的监控与管理能力。 + + +### 核心特性 + +| 特性 | 说明 | +| --- | --- | +| **原生 MTML API** | 直接封装 libmtml.so C 库函数 | +| **NVML 兼容层** | 提供 pynvml 的替代接口,支持一行替换 | +| **上下文管理器** | 提供 `with` 语句管理 GPU/Memory/VPU 子组件生命周期 | +| **多 GPU 支持** | 支持多卡拓扑查询、P2P 状态检测、MtLink 互连检测 | +| **框架兼容** | 兼容 sglang 等 AI 推理框架的 NVML 调用 | + +### 文件结构 + +```text +mthreads-ml-py/ +├── pymtml.py # 核心库,包含所有 MTML 绑定和 NVML 兼容函数 +├── mtml_2.2.0.h # MTML C 语言头文件(API 参考) +├── setup.py # PyPI 打包配置 +├── Makefile # 构建/测试/发布脚本 +├── example.py # 基本用法示例 +├── test_pymtml.py # 原生 MTML API 测试 +├── test_pynvml.py # NVML 兼容层测试 +├── test_sglang_compat.py # sglang 框架兼容性测试 +└── examples/ # 分类 API 使用示例(含运行输出) + ├── 01_library_basics.py + ├── 02_device_info.py + ... + └── 13_comprehensive_report.py +``` + +--- + +## 2. 环境要求 + +- **Python** 3.7+ +- **MTML** 2.2.0+ +- **硬件** 摩尔线程 GPU +- **驱动** 已安装摩尔线程 GPU 驱动程序 + +验证驱动是否安装: + +```bash +# 检查 libmtml.so 是否可用 +ldconfig -p | grep libmtml + +# 检查 GPU 设备 +ls /dev/dri/render* +``` + +--- + +## 3. 安装方式 + +### 方式一:通过 pip 安装(推荐) + +```bash +pip install mthreads-ml-py +``` + +### 方式二:从源码安装 + +```bash +git clone https://github.com/MooreThreads/mthreads-ml-py.git +cd mthreads-ml-py +pip install -e . +``` + +### 方式三:构建 wheel 包 + +```bash +make build # 构建 wheel +pip install dist/*.whl +``` + +--- + +## 4. 快速上手 + +### 4.1 最简示例 + +```python +from pymtml import * + +mtmlLibraryInit() + +device_count = mtmlLibraryCountDevice() +print(f"Found {device_count} GPU(s)") + +for i in range(device_count): + device = mtmlLibraryInitDeviceByIndex(i) + name = mtmlDeviceGetName(device) + uuid = mtmlDeviceGetUUID(device) + print(f"Device {i}: {name} ({uuid})") + +mtmlLibraryShutDown() +``` + +### 4.2 查询设备详细信息 + +```python +from pymtml import * + +mtmlLibraryInit() + +for i in range(mtmlLibraryCountDevice()): + device = mtmlLibraryInitDeviceByIndex(i) + + # 基本信息 + name = mtmlDeviceGetName(device) + uuid = mtmlDeviceGetUUID(device) + print(f"Device {i}: {name} ({uuid})") + + # 显存信息(使用上下文管理器自动管理资源) + with mtmlMemoryContext(device) as memory: + total = mtmlMemoryGetTotal(memory) + used = mtmlMemoryGetUsed(memory) + print(f" Memory: {used / 1024**3:.2f} / {total / 1024**3:.2f} GB") + + # GPU 利用率和温度 + with mtmlGpuContext(device) as gpu: + util = mtmlGpuGetUtilization(gpu) + temp = mtmlGpuGetTemperature(gpu) + print(f" GPU Util: {util}%, Temp: {temp}°C") + +mtmlLibraryShutDown() +``` + +运行输出格式示例: + +```text +Found N GPU(s) +Device 0: <设备名称> () + Memory: <已用> / <总量> GB + GPU Util: %, Temp: °C +``` + +### 4.3 作为 pynvml 替代使用 + +只需修改一行 import 即可将已有 pynvml 代码迁移到摩尔线程平台: + +```python +# 替换前: +# import pynvml + +# 替换后: +import pymtml as pynvml + +# 之后的代码完全不用改动 +pynvml.nvmlInit() +device_count = pynvml.nvmlDeviceGetCount() +for i in range(device_count): + handle = pynvml.nvmlDeviceGetHandleByIndex(i) + name = pynvml.nvmlDeviceGetName(handle) + mem_info = pynvml.nvmlDeviceGetMemoryInfo(handle) + print(f"Device {i}: {name}") + print(f" Memory: {mem_info.used / 1024**3:.2f} / {mem_info.total / 1024**3:.2f} GB") + print(f" Free: {mem_info.free / 1024**3:.2f} GB") +pynvml.nvmlShutdown() +``` + +--- + +## 5. 原生 MTML API 详细说明 + +### 5.1 库生命周期 + +| 函数 | 说明 | +| --- | --- | +| `mtmlLibraryInit()` | 初始化 MTML 库,加载 `libmtml.so` | +| `mtmlLibraryShutDown()` | 关闭库接口(库本身保持加载) | +| `mtmlLibraryGetVersion()` | 获取 MTML 库版本号 | +| `mtmlLibraryCountDevice()` | 获取 GPU 设备数量 | +| `mtmlLibraryInitDeviceByIndex(index)` | 按索引获取设备句柄 | +| `mtmlLibraryInitDeviceByUuid(uuid)` | 按 UUID 获取设备句柄 | +| `mtmlLibraryInitDeviceByPciSbdf(sbdf)` | 按 PCI SBDF 获取设备句柄 | +| `mtmlLibraryInitSystem()` | 初始化系统句柄 | + +```python +mtmlLibraryInit() + +version = mtmlLibraryGetVersion() +count = mtmlLibraryCountDevice() +print(f"MTML v{version}, {count} device(s)") + +system = mtmlLibraryInitSystem() +driver_ver = mtmlSystemGetDriverVersion(system) +print(f"Driver: {driver_ver}") +mtmlLibraryFreeSystem(system) + +mtmlLibraryShutDown() +``` + +### 5.2 设备信息 API + +| 函数 | 返回值 | 说明 | +| --- | --- | --- | +| `mtmlDeviceGetIndex(device)` | `int` | 设备索引 | +| `mtmlDeviceGetName(device)` | `str` | 设备名称 | +| `mtmlDeviceGetUUID(device)` | `str` | 设备 UUID | +| `mtmlDeviceGetBrand(device)` | `int` | 品牌枚举值 | +| `mtmlDeviceGetSerialNumber(device)` | `str` | 序列号 | +| `mtmlDeviceGetPciInfo(device)` | `c_mtmlPciInfo_t` | PCI 信息结构体 | +| `mtmlDeviceGetPowerUsage(device)` | `int` | 功耗(毫瓦) | +| `mtmlDeviceGetVbiosVersion(device)` | `str` | VBIOS 版本 | +| `mtmlDeviceGetMtBiosVersion(device)` | `str` | MtBIOS 版本 | +| `mtmlDeviceCountGpuCores(device)` | `int` | GPU 核心数量 | +| `mtmlDeviceGetGpuPath(device)` | `str` | GPU 设备路径 | +| `mtmlDeviceGetPrimaryPath(device)` | `str` | 主设备路径 | +| `mtmlDeviceGetRenderPath(device)` | `str` | 渲染设备路径 | +| `mtmlDeviceGetProperty(device)` | `c_mtmlDeviceProperty_t` | 设备属性 | + +```python +device = mtmlLibraryInitDeviceByIndex(0) + +name = mtmlDeviceGetName(device) +uuid = mtmlDeviceGetUUID(device) +power = mtmlDeviceGetPowerUsage(device) # 毫瓦 +cores = mtmlDeviceCountGpuCores(device) + +pci = mtmlDeviceGetPciInfo(device) +print(f"PCI: {pci.sbdf}, Bus Width: {pci.busWidth}") +print(f"PCIe Gen: {pci.pciCurGen}/{pci.pciMaxGen}") +print(f"PCIe Width: x{pci.pciCurWidth}/x{pci.pciMaxWidth}") +``` + +### 5.3 GPU API + +GPU 是设备的子组件,需要先初始化、使用完后释放。推荐使用上下文管理器。 + +| 函数 | 返回值 | 说明 | +| --- | --- | --- | +| `mtmlDeviceInitGpu(device)` | gpu 句柄 | 初始化 GPU 子组件 | +| `mtmlDeviceFreeGpu(gpu)` | None | 释放 GPU 子组件 | +| `mtmlGpuGetUtilization(gpu)` | `int` | GPU 利用率(0-100%) | +| `mtmlGpuGetTemperature(gpu)` | `int` | GPU 温度(°C) | +| `mtmlGpuGetClock(gpu)` | `int` | 当前 GPU 时钟频率(MHz) | +| `mtmlGpuGetMaxClock(gpu)` | `int` | 最大 GPU 时钟频率(MHz) | +| `mtmlGpuGetEngineUtilization(gpu, engine)` | `int` | 指定引擎利用率 | + +```python +# 方式一:手动管理 +gpu = mtmlDeviceInitGpu(device) +print(f"Utilization: {mtmlGpuGetUtilization(gpu)}%") +print(f"Temperature: {mtmlGpuGetTemperature(gpu)}°C") +print(f"Clock: {mtmlGpuGetClock(gpu)} / {mtmlGpuGetMaxClock(gpu)} MHz") +mtmlDeviceFreeGpu(gpu) + +# 方式二:上下文管理器(推荐) +with mtmlGpuContext(device) as gpu: + print(f"Utilization: {mtmlGpuGetUtilization(gpu)}%") +``` + +GPU 引擎类型枚举: + +```python +MTML_GPU_ENGINE_GEOMETRY = 0 # 几何引擎 +MTML_GPU_ENGINE_2D = 1 # 2D 引擎 +MTML_GPU_ENGINE_3D = 2 # 3D 引擎 +MTML_GPU_ENGINE_COMPUTE = 3 # 计算引擎 +``` + +### 5.4 Memory API + +| 函数 | 返回值 | 说明 | +| --- | --- | --- | +| `mtmlDeviceInitMemory(device)` | memory 句柄 | 初始化 Memory 子组件 | +| `mtmlDeviceFreeMemory(memory)` | None | 释放 Memory 子组件 | +| `mtmlMemoryGetTotal(memory)` | `int` | 总显存(字节) | +| `mtmlMemoryGetUsed(memory)` | `int` | 已用显存(字节) | +| `mtmlMemoryGetUtilization(memory)` | `int` | 显存利用率(0-100%) | +| `mtmlMemoryGetClock(memory)` | `int` | 当前显存时钟(MHz) | +| `mtmlMemoryGetMaxClock(memory)` | `int` | 最大显存时钟(MHz) | +| `mtmlMemoryGetBusWidth(memory)` | `int` | 显存总线宽度(bits) | +| `mtmlMemoryGetBandwidth(memory)` | `int` | 显存带宽(GB/s) | +| `mtmlMemoryGetSpeed(memory)` | `int` | 显存速率(Mbps) | +| `mtmlMemoryGetVendor(memory)` | `str` | 显存供应商 | +| `mtmlMemoryGetType(memory)` | `int` | 显存类型枚举 | +| `mtmlMemoryGetUsedSystem(memory)` | `int` | 系统已用内存(字节) | + +```python +with mtmlMemoryContext(device) as memory: + total = mtmlMemoryGetTotal(memory) + used = mtmlMemoryGetUsed(memory) + free = total - used + util = mtmlMemoryGetUtilization(memory) + print(f"Memory: {used/1024**3:.2f}/{total/1024**3:.2f} GB ({util}%)") + print(f"Bus Width: {mtmlMemoryGetBusWidth(memory)} bits") + print(f"Bandwidth: {mtmlMemoryGetBandwidth(memory)} GB/s") + print(f"Vendor: {mtmlMemoryGetVendor(memory)}") +``` + +### 5.5 VPU API(视频处理单元) + +| 函数 | 返回值 | 说明 | +| --- | --- | --- | +| `mtmlDeviceInitVpu(device)` | vpu 句柄 | 初始化 VPU 子组件 | +| `mtmlDeviceFreeVpu(vpu)` | None | 释放 VPU 子组件 | +| `mtmlVpuGetClock(vpu)` | `int` | 当前 VPU 时钟(MHz) | +| `mtmlVpuGetMaxClock(vpu)` | `int` | 最大 VPU 时钟(MHz) | +| `mtmlVpuGetUtilization(vpu)` | `c_mtmlCodecUtil_t` | 编解码利用率 | +| `mtmlVpuGetCodecCapacity(vpu)` | `(int, int)` | (编码容量, 解码容量) | + +```python +with mtmlVpuContext(device) as vpu: + clock = mtmlVpuGetClock(vpu) + util = mtmlVpuGetUtilization(vpu) + enc_cap, dec_cap = mtmlVpuGetCodecCapacity(vpu) + print(f"VPU Clock: {clock} MHz") + print(f"Encode Util: {util.encodeUtil}%, Decode Util: {util.decodeUtil}%") + print(f"Codec Capacity: Enc={enc_cap}, Dec={dec_cap}") +``` + +### 5.6 MtLink API(多 GPU 互连) + +| 函数 | 说明 | +| --- | --- | +| `mtmlDeviceGetMtLinkSpec(device)` | 获取 MtLink 规格 | +| `mtmlDeviceGetMtLinkState(device, link)` | 获取指定链路状态 | +| `mtmlDeviceGetMtLinkRemoteDevice(device, link)` | 获取远程连接的设备 | +| `mtmlDeviceCountMtLinkLayouts(dev1, dev2)` | 获取两设备间的链路数 | +| `mtmlDeviceGetMtLinkLayouts(dev1, dev2, count)` | 获取链路布局详情 | + +```python +spec = mtmlDeviceGetMtLinkSpec(device) +print(f"MtLink: version={spec.version}, bandwidth={spec.bandWidth}, links={spec.linkNum}") + +for i in range(spec.linkNum): + state = mtmlDeviceGetMtLinkState(device, i) + state_str = {0: "DOWN", 1: "UP", 2: "DOWNGRADE"}.get(state, "UNKNOWN") + print(f" Link {i}: {state_str}") +``` + +### 5.7 拓扑与 P2P API + +| 函数 | 说明 | +| --- | --- | +| `mtmlDeviceGetTopologyLevel(dev1, dev2)` | 获取两设备间拓扑层级 | +| `mtmlDeviceGetP2PStatus(dev1, dev2, cap)` | 获取 P2P 读写状态 | +| `mtmlDeviceCountDeviceByTopologyLevel(dev, level)` | 统计指定层级的设备数 | +| `mtmlDeviceGetDeviceByTopologyLevel(dev, level, count)` | 获取指定层级的设备列表 | + +拓扑层级枚举: + +```python +MTML_TOPOLOGY_INTERNAL = 0 # 同一 GPU +MTML_TOPOLOGY_SINGLE = 1 # 单 PCIe 交换机 +MTML_TOPOLOGY_MULTIPLE = 2 # 多 PCIe 交换机 +MTML_TOPOLOGY_HOSTBRIDGE = 3 # 主桥 +MTML_TOPOLOGY_NODE = 4 # 同一 NUMA 节点 +MTML_TOPOLOGY_SYSTEM = 5 # 不同 NUMA 节点 +``` + +### 5.8 ECC API + +| 函数 | 说明 | +| --- | --- | +| `mtmlMemoryGetEccMode(memory)` | 获取 ECC 模式 (当前, 待定) | +| `mtmlMemoryGetEccErrorCounter(memory, errorType, counterType, location)` | 获取 ECC 错误计数 | +| `mtmlMemoryGetRetiredPagesCount(memory)` | 获取退役页面计数 | +| `mtmlMemoryGetRetiredPagesPendingStatus(memory)` | 获取退役页面挂起状态 | +| `mtmlMemoryClearEccErrorCounts(memory, counterType)` | 清除 ECC 错误计数 | + +```python +with mtmlMemoryContext(device) as memory: + current_mode, pending_mode = mtmlMemoryGetEccMode(memory) + print(f"ECC: current={'ON' if current_mode else 'OFF'}, " + f"pending={'ON' if pending_mode else 'OFF'}") + + corrected = mtmlMemoryGetEccErrorCounter( + memory, MTML_MEMORY_ERROR_TYPE_CORRECTED, + MTML_VOLATILE_ECC, MTML_MEMORY_LOCATION_DRAM) + uncorrected = mtmlMemoryGetEccErrorCounter( + memory, MTML_MEMORY_ERROR_TYPE_UNCORRECTED, + MTML_VOLATILE_ECC, MTML_MEMORY_LOCATION_DRAM) + print(f"ECC Errors: corrected={corrected}, uncorrected={uncorrected}") +``` + +### 5.9 风扇 API + +```python +fan_count = mtmlDeviceCountFan(device) +for i in range(fan_count): + speed = mtmlDeviceGetFanSpeed(device, i) # 百分比 + rpm = mtmlDeviceGetFanRpm(device, i) # RPM + print(f"Fan {i}: {speed}% ({rpm} RPM)") +``` + +### 5.10 MPC API(多 GPU 配置分区) + +```python +mode = mtmlDeviceGetMpcMode(device) +profile_count = mtmlDeviceCountSupportedMpcProfiles(device) +config_count = mtmlDeviceCountSupportedMpcConfigurations(device) +instance_count = mtmlDeviceCountMpcInstances(device) +``` + +### 5.11 虚拟化 API + +```python +prop = mtmlDeviceGetProperty(device) +if prop.virtCapability == MTML_DEVICE_SUPPORT_VIRTUALIZATION: + virt_type_count = mtmlDeviceCountSupportedVirtTypes(device) + active_count = mtmlDeviceCountActiveVirtDevices(device) + print(f"Virtualization: {virt_type_count} types, {active_count} active") +``` + +--- + +## 6. NVML 兼容层 + +pymtml 提供了完整的 pynvml 兼容接口,支持以 `import pymtml as pynvml` 的方式实现零改动迁移。 + +### 6.1 NVML 兼容函数映射 + +| NVML 函数 | MTML 对应 | 说明 | +| --- | --- | --- | +| `nvmlInit()` | `mtmlLibraryInit()` | 初始化 | +| `nvmlShutdown()` | `mtmlLibraryShutDown()` | 关闭 | +| `nvmlDeviceGetCount()` | `mtmlLibraryCountDevice()` | 设备数量 | +| `nvmlDeviceGetHandleByIndex(i)` | `mtmlLibraryInitDeviceByIndex(i)` | 按索引获取设备 | +| `nvmlDeviceGetHandleByUuid(uuid)` | `mtmlLibraryInitDeviceByUuid(uuid)` | 按 UUID 获取设备 | +| `nvmlDeviceGetHandleByPciBusId(id)` | `mtmlLibraryInitDeviceByPciSbdf(id)` | 按 PCI 地址获取设备 | +| `nvmlDeviceGetName(dev)` | `mtmlDeviceGetName(dev)` | 设备名称 | +| `nvmlDeviceGetUUID(dev)` | `mtmlDeviceGetUUID(dev)` | 设备 UUID | +| `nvmlDeviceGetMemoryInfo(dev)` | 内部组合调用 | 返回 `NVMLMemoryInfo(total, free, used)` | +| `nvmlDeviceGetUtilizationRates(dev)` | 内部组合调用 | 返回 `NVMLUtilization(gpu, memory)` | +| `nvmlDeviceGetTemperature(dev, type)` | `mtmlGpuGetTemperature()` | GPU 温度 | +| `nvmlDeviceGetPowerUsage(dev)` | `mtmlDeviceGetPowerUsage()` | 功耗 | +| `nvmlDeviceGetClockInfo(dev, type)` | 按类型分发 | GPU/MEM/VIDEO 时钟 | +| `nvmlDeviceGetFanSpeed(dev)` | `mtmlDeviceGetFanSpeed()` | 风扇转速 | +| `nvmlDeviceGetPciInfo(dev)` | `mtmlDeviceGetPciInfo()` | PCI 信息 | +| `nvmlDeviceGetP2PStatus(d1, d2, cap)` | 多种 MTML 调用组合 | P2P 状态 | +| `nvmlDeviceGetTopologyCommonAncestor(d1, d2)` | `mtmlDeviceGetTopologyLevel()` | 拓扑公共祖先 | +| `nvmlDeviceGetCudaComputeCapability(dev)` | 需 `torch_musa` | MUSA 计算能力 | +| `nvmlSystemGetDriverVersion()` | `mtmlSystemGetDriverVersion()` | 驱动版本 | + +### 6.2 NVML 兼容数据类型 + +```python +# 内存信息(dataclass) +NVMLMemoryInfo(total=25769803776, free=25488990208, used=280813568) + +# 利用率信息(dataclass) +NVMLUtilization(gpu=0, memory=1) + +# 错误类型别名 +NVMLError = MTMLError +NVMLError_NotFound = MTMLError_NotFound +# ... 等 +``` + +### 6.3 不支持的 NVML 函数 + +以下函数因硬件差异返回固定值或空值: + +| 函数 | 返回值 | 原因 | +| --- | --- | --- | +| `nvmlSystemGetCudaDriverVersion()` | `0` | 摩尔线程使用 MUSA 而非 CUDA | +| `nvmlDeviceGetBAR1MemoryInfo()` | `"N/A"` | 不支持 | +| `nvmlDeviceGetDisplayMode()` | `0` | 不支持 | +| `nvmlDeviceGetPersistenceMode()` | `0` | 不支持 | +| `nvmlDeviceGetPerformanceState()` | `"N/A"` | 不支持 | +| `nvmlDeviceGetPowerManagementLimit()` | `0` | 不支持 | +| `nvmlDeviceGetComputeRunningProcesses()` | `[]` | 不支持 | +| `nvmlDeviceGetMigMode()` | `[0, 0]` | 不支持 MIG | +| `nvmlDeviceGetCudaComputeCapability()` | `(0, 0)` | 需要 `torch_musa` | + +--- + +## 7. 上下文管理器 + +pymtml 提供三个上下文管理器,自动管理子组件的初始化和释放: + +```python +# GPU 上下文管理器 +with mtmlGpuContext(device) as gpu: + util = mtmlGpuGetUtilization(gpu) + temp = mtmlGpuGetTemperature(gpu) + +# Memory 上下文管理器 +with mtmlMemoryContext(device) as memory: + total = mtmlMemoryGetTotal(memory) + used = mtmlMemoryGetUsed(memory) + +# VPU 上下文管理器 +with mtmlVpuContext(device) as vpu: + clock = mtmlVpuGetClock(vpu) +``` + +使用上下文管理器可以确保即使在发生异常时,子组件资源也能被正确释放,避免资源泄漏。 + +--- + +## 8. 错误处理 + +所有 MTML 函数在失败时抛出 `MTMLError`(别名 `NVMLError`)异常: + +```python +try: + device = mtmlLibraryInitDeviceByIndex(99) # 不存在的设备 +except MTMLError as e: + print(f"Error code: {e.value}") + print(f"Error message: {e}") +``` + +### 错误码 + +| 常量 | 值 | 说明 | +| --- | --- | --- | +| `MTML_SUCCESS` | 0 | 成功 | +| `MTML_ERROR_DRIVER_NOT_LOADED` | 1 | 驱动未加载 | +| `MTML_ERROR_DRIVER_FAILURE` | 2 | 驱动故障 | +| `MTML_ERROR_INVALID_ARGUMENT` | 3 | 无效参数 | +| `MTML_ERROR_NOT_SUPPORTED` | 4 | 不支持的操作 | +| `MTML_ERROR_NO_PERMISSION` | 5 | 权限不足 | +| `MTML_ERROR_INSUFFICIENT_SIZE` | 6 | 缓冲区不足 | +| `MTML_ERROR_NOT_FOUND` | 7 | 未找到 | +| `MTML_ERROR_INSUFFICIENT_MEMORY` | 8 | 内存不足 | +| `MTML_ERROR_DRIVER_TOO_OLD` | 9 | 驱动版本过旧 | +| `MTML_ERROR_DRIVER_TOO_NEW` | 10 | 驱动版本过新 | +| `MTML_ERROR_TIMEOUT` | 11 | 超时 | +| `MTML_ERROR_RESOURCE_IS_BUSY` | 12 | 资源繁忙 | +| `MTML_ERROR_UNKNOWN` | 999 | 未知错误 | + +每个错误码对应一个异常子类,可精确捕获: + +```python +try: + mtmlDeviceGetMtLinkSpec(device) +except MTMLError_NotSupported: + print("MtLink not supported on this device") +except MTMLError as e: + print(f"Other error: {e}") +``` + +--- + +## 9. 常量与枚举 + +### 品牌类型 + +```python +MTML_BRAND_MTT = 0 +MTML_BRAND_UNKNOWN = 1 +``` + +### 显存类型 + +```python +MTML_MEM_TYPE_LPDDR4 = 0 +MTML_MEM_TYPE_GDDR6 = 1 +``` + +### MtLink 状态 + +```python +MTML_MTLINK_STATE_DOWN = 0 # 断开 +MTML_MTLINK_STATE_UP = 1 # 连接 +MTML_MTLINK_STATE_DOWNGRADE = 2 # 降级 +``` + +### P2P 能力 + +```python +MTML_P2P_CAPS_READ = 0 # 读能力 +MTML_P2P_CAPS_WRITE = 1 # 写能力 +``` + +### ECC 相关 + +```python +MTML_MEMORY_ECC_DISABLE = 0 +MTML_MEMORY_ECC_ENABLE = 1 + +MTML_VOLATILE_ECC = 0 # 当前会话计数 +MTML_AGGREGATE_ECC = 1 # 累计计数 + +MTML_MEMORY_ERROR_TYPE_CORRECTED = 0 # 已纠正错误 +MTML_MEMORY_ERROR_TYPE_UNCORRECTED = 1 # 未纠正错误 + +MTML_MEMORY_LOCATION_DRAM = 0x1 # DRAM 位置 +``` + +### NVML 兼容常量 + +```python +# 时钟类型 +NVML_CLOCK_GRAPHICS = 0 +NVML_CLOCK_SM = 1 +NVML_CLOCK_MEM = 2 +NVML_CLOCK_VIDEO = 3 + +# 温度传感器 +NVML_TEMPERATURE_GPU = 0 + +# 拓扑层级(注意数值与 MTML 不同) +NVML_TOPOLOGY_INTERNAL = 0 +NVML_TOPOLOGY_SINGLE = 10 +NVML_TOPOLOGY_MULTIPLE = 20 +NVML_TOPOLOGY_HOSTBRIDGE = 30 +NVML_TOPOLOGY_NODE = 40 +NVML_TOPOLOGY_SYSTEM = 50 + +# P2P Caps Index +NVML_P2P_CAPS_INDEX_READ = 0 +NVML_P2P_CAPS_INDEX_WRITE = 1 +NVML_P2P_CAPS_INDEX_NVLINK = 2 # 映射到 MtLink +``` + +--- + +## 10. 示例程序与运行输出 + +`examples/` 目录包含 13 个独立示例,覆盖所有支持的 API 类别。以下为**输出格式示例**(已脱敏,不含具体硬件型号与关键信息)。 + +### 运行方式 + +```bash +# 运行单个示例 +python examples/01_library_basics.py + +# 运行所有示例 +for f in examples/[0-9]*.py; do python "$f"; echo; done +``` + +### 01 — 库基础操作 (`01_library_basics.py`) + +演示初始化、版本查询、设备枚举、按 UUID/PCI 获取设备、init/shutdown 循环。 + +```text +============================================================ + 示例 01: 库基础操作 +============================================================ +[1] 库初始化成功 +[2] MTML 库版本: +[3] 驱动版本: <驱动版本号> +[4] 检测到 N 个 GPU 设备 + 设备 0: <设备名称> (UUID: ) +[5] 按 UUID 获取设备: <设备名称> +[6] 按 PCI SBDF (<段:总线:设备.功能>) 获取设备: <设备名称> +[7] 库关闭成功 +[8] 测试多次 init/shutdown 循环: + 第 1 次循环: 检测到 N 个设备 ✓ + 第 2 次循环: 检测到 N 个设备 ✓ + 第 3 次循环: 检测到 N 个设备 ✓ +``` + +### 02 — 设备信息查询 (`02_device_info.py`) + +查询名称、UUID、品牌、序列号、PCI 信息、核心数、功耗、设备属性等。 + +```text +============================================================ + 示例 02: 设备信息查询 +============================================================ + +--- 设备 0 --- + 名称: <设备名称> + 索引: 0 + UUID: + 品牌: + 序列号: <序列号> + VBIOS 版本: + MtBIOS 版本: + GPU 核心数: + 功耗: mW ( W) + PCI SBDF: <段:总线:设备.功能> + PCI Bus ID: <段:总线:设备.功能> + PCI 设备ID: 0x<十六进制> + PCIe 代数: 当前 Gen / 最大 Gen + PCIe 宽度: 当前 x / 最大 x + PCIe 速率: 当前 GT/s / 最大 GT/s + PCIe 插槽: <插槽名> (type=) + 虚拟化: <支持 | 不支持> + MPC: <支持 | 不支持> +``` + +### 03 — GPU 监控 (`03_gpu_monitoring.py`) + +查询 GPU 利用率、温度、时钟频率及各引擎(几何/2D/3D/计算)利用率。 + +```text +============================================================ + 示例 03: GPU 监控 +============================================================ + +--- 设备 0: <设备名称> --- + GPU 利用率: <0-100>% + GPU 温度: °C + GPU 时钟: <当前> / <最大> MHz + 引擎利用率: + 几何引擎: <0-100>% + 2D 引擎: <0-100>% + 3D 引擎: <0-100>% + 计算引擎: <0-100>% +``` + +### 04 — 显存监控 (`04_memory_monitoring.py`) + +查询显存总量/已用/空闲、带宽、总线宽度、时钟、类型、供应商。 + +```text +============================================================ + 示例 04: 显存监控 +============================================================ + +--- 设备 0: <设备名称> --- + 显存总量: GB ( MB) + 已用显存: GB ( MB) + 空闲显存: GB ( MB) + 显存利用率: <0-100>% + 显存时钟: <当前> / <最大> MHz + 总线宽度: bits + 显存带宽: GB/s + 显存速率: Mbps + 显存类型: + 显存供应商: <供应商名> + 系统占用显存: MB 或 [不可用: Not Supported] +``` + +### 05 — VPU 监控 (`05_vpu_monitoring.py`) + +查询视频处理单元的时钟、编解码利用率、容量和会话状态。 + +```text +============================================================ + 示例 05: VPU 监控 +============================================================ + +--- 设备 0: <设备名称> --- + VPU 时钟: <当前> / <最大> MHz + 编码利用率: <0-100>% + 解码利用率: <0-100>% + 编码容量: + 解码容量: + 活跃编码会话: + 活跃解码会话: +``` + +### 06 — 风扇与功耗 (`06_fan_and_power.py`) + +查询各风扇的转速百分比和 RPM,以及设备功耗。 + +```text +============================================================ + 示例 06: 风扇与功耗监控 +============================================================ + +--- 设备 0: <设备名称> --- + 当前功耗: mW ( W) + 风扇数量: + 风扇 0 转速: <0-100>% + 风扇 0 RPM: + 风扇 1 转速: <0-100>% + 风扇 1 RPM: + ... +``` + +### 07 — ECC 错误查询 (`07_ecc_errors.py`) + +查询 ECC 模式、纠正/未纠正错误计数、退役页面。 + +```text +============================================================ + 示例 07: ECC 错误查询 +============================================================ + +--- 设备 0: <设备名称> --- + ECC 当前模式: <启用 | 禁用> + ECC 待定模式: <启用 | 禁用> + 已纠正错误 (当前会话): 或 [不可用: Not Supported] + 未纠正错误 (当前会话): 或 [不可用: Not Supported] + 已纠正错误 (累计): 或 [不可用: Not Supported] + 未纠正错误 (累计): 或 [不可用: Not Supported] + 退役页面 (单比特/双比特): / 或 [不可用] + 退役页面挂起: <是 | 否> +``` + +> 注: 部分型号不支持 ECC,会显示 Not Supported;支持 ECC 的型号会返回实际计数。 + +### 08 — 拓扑与 MtLink 互连 (`08_topology_and_mtlink.py`) + +查询 MtLink 规格与状态、多 GPU 拓扑关系、P2P 状态。 + +```text +============================================================ + 示例 08: 拓扑与 MtLink 互连 +============================================================ + +检测到 N 个 GPU 设备 + 设备 0: <设备名称> + 设备 1: <设备名称> + ... + +--- MtLink 信息 --- + 设备 N MtLink 规格: version=, bandWidth=, linkNum= + 链路 0/1/...: <连接 | 断开 | 降级> [ -> <远程设备名> ] + +--- 拓扑关系矩阵 --- (需 2+ GPU) + 设备 i <-> 设备 j: <同一GPU | 单PCIe交换机 | ... | 跨NUMA节点> + +--- P2P 状态 --- (需 2+ GPU) + 设备 i <-> 设备 j: 读=, 写= +``` + +> 注: 单卡环境无 MtLink;多卡环境将展示链路状态、拓扑矩阵和 P2P 连通性。 + +### 09 — NVML 兼容层 (`09_nvml_compatibility.py`) + +以 `import pymtml as pynvml` 方式使用全部 NVML 兼容 API。 + +```text +============================================================ + 示例 09: NVML 兼容层 (import pymtml as pynvml) +============================================================ +[1] nvmlInit() 成功 +[2] 驱动版本: <驱动版本号> +[3] GPU 数量: + +--- GPU 0 --- + 名称: <设备名称> + UUID: + 索引: 0 + PCI SBDF: <段:总线:设备.功能> + PCI BusId: <段:总线:设备.功能> + 显存总量: GB + 已用显存: GB + 空闲显存: GB + GPU 利用率: <0-100>% + 显存利用率: <0-100>% + 温度: °C + 功耗: W + GPU 时钟: MHz + 显存时钟: MHz + 视频时钟: MHz + GPU 最大时钟: MHz + 显存最大时钟: MHz + 风扇转速: <0-100>% + 编码器/解码器利用率: <0-100>% + GPU 核心数: + 显存总线宽度: bits + VBIOS 版本: + Minor Number: + ECC 模式: 当前=<启用|禁用>, 待定=<启用|禁用> + MUSA 计算能力: 或 不可用 (需要 torch_musa) + +[4] nvmlShutdown() 成功 +``` + +### 10 — 设备路径与显示接口 (`10_device_paths.py`) + +查询 GPU/Primary/Render 设备路径及显示接口规格。 + +```text +============================================================ + 示例 10: 设备路径与显示接口 +============================================================ + +--- 设备 0: <设备名称> --- + GPU 路径: /dev/mtgpu. + Primary 路径: /dev/dri/card + Render 路径: /dev/dri/renderD + 显示接口数量: + 接口 0: , 最大分辨率 <宽>x<高> + 接口 1: ... +``` + +### 11 — MPC 与虚拟化 (`11_mpc_and_virtualization.py`) + +查询 MPC 配置分区和虚拟化能力。 + +```text +============================================================ + 示例 11: MPC 与虚拟化 +============================================================ + +--- 设备 0: <设备名称> --- + MPC 支持: <是 | 否> + MPC 类型: <无 | 父设备 | MPC 实例> + MPC 模式: <启用 | 禁用> + MPC 配置文件: 个 或 [不可用] + MPC 配置方案: 个 或 [不可用] + MPC 实例数: 或 [不可用] + + 虚拟化支持: <是 | 否> + 虚拟化角色: <无 | 宿主机虚拟设备 | 虚拟机虚拟设备> + 支持的虚拟化类型: 个 或 [不可用] + 可用的虚拟化类型: 个 + 活跃虚拟设备: 个 +``` + +> 注: 部分型号不支持 MPC/虚拟化;支持的型号会返回配置与实例信息。 + +### 12 — CPU 亲和性与日志配置 (`12_affinity_and_log.py`) + +查询 GPU 的 CPU/内存 NUMA 亲和性及 MTML 日志配置。 + +```text +============================================================ + 示例 12: CPU 亲和性与日志配置 +============================================================ + +--- 设备 0: <设备名称> --- + CPU 亲和性掩码: ['0x', ...] + CPU 核心列表: [<核心编号>, ...] + 内存亲和性: [, ...] 或 [不可用] + +--- 日志配置 --- + 日志文件: <路径> + 最大大小: + 日志级别: <关闭|致命|错误|警告|信息> +``` + +### 13 — 综合 GPU 报告 (`13_comprehensive_report.py`) + +类似 nvidia-smi 的一站式信息汇总。 + +```text +======================================================================== + MTML GPU 综合报告 +======================================================================== + MTML 版本: 驱动版本: <版本> GPU 数量: +======================================================================== + + GPU 0: <设备名称> + ├─ UUID: + ├─ PCI: <段:总线:设备.功能> (Gen x) + ├─ 功耗: W + ├─ 温度: °C 风扇: % + ├─ GPU: 利用率 % 时钟 <当前>/<最大> MHz + └─ 显存: <已用>/<总量> GB (%) 时钟 <当前>/<最大> MHz + +======================================================================== +``` + +--- + +## 11. 测试 + +### 运行所有测试 + +```bash +make test # 使用 pytest 运行 +``` + +### 逐个运行测试 + +```bash +python test_pymtml.py # 原生 MTML API 测试 +python test_pynvml.py # NVML 兼容层测试 +python test_sglang_compat.py # sglang 框架兼容性测试 +``` + +### 测试说明 + +- **test_pymtml.py** — 测试所有原生 MTML API,包括设备查询、GPU/Memory/VPU 子组件、MtLink、ECC、MPC、虚拟化、拓扑等,还包含 init/shutdown 循环测试 +- **test_pynvml.py** — 以 `import pymtml as pynvml` 方式测试所有 NVML 兼容函数 +- **test_sglang_compat.py** — 测试 sglang 推理框架依赖的关键 API,包括 P2P 常量验证、NVLink/MtLink 检测、拓扑查询 + +> 注意:所有测试需要摩尔线程 GPU 硬件及驱动。 + + +--- + +## 12 常见问题 + +### Q: 运行时报 "Driver Not Loaded" + +```text +pymtml.MTMLError_DriverNotLoaded: Driver Not Loaded +``` + +**原因**:摩尔线程 GPU 驱动未加载或不在当前环境中可用(例如在沙箱、容器中运行时)。 + +**解决**:确认 GPU 驱动已安装,`libmtml.so` 在 `LD_LIBRARY_PATH` 中。 + +### Q: `nvmlDeviceGetCudaComputeCapability()` 返回 `(0, 0)` + +**原因**:该函数需要 `torch` 和 `torch_musa` 才能获取 MUSA 计算能力。 + +**解决**:安装 `torch_musa` 后即可正常返回。 + +### Q: 如何在 sglang 中使用? + +在 sglang 代码中将 `import pynvml` 改为 `import pymtml as pynvml`,其余代码无需修改。pymtml 已实现 sglang 依赖的所有 NVML API,包括 P2P 检测和拓扑查询。 + +### Q: MtLink 查询返回 N/A + +**原因**:当前设备不支持 MtLink(单卡或无 MtLink 硬件)。MtLink 是摩尔线程的多卡互连技术,类似 NVIDIA 的 NVLink。 + +### Q: 库的 init/shutdown 可以多次调用吗? + +可以。pymtml 支持多次 init/shutdown 循环调用,内部通过引用计数管理库生命周期。 diff --git a/examples/01_library_basics.py b/examples/01_library_basics.py new file mode 100644 index 0000000..2392f19 --- /dev/null +++ b/examples/01_library_basics.py @@ -0,0 +1,62 @@ +""" +示例 01: 库基础操作 +演示 MTML 库的初始化、版本查询、设备枚举、关闭等基础操作, +以及多次 init/shutdown 循环调用。 +""" + +from pymtml import * + +print("=" * 60) +print(" 示例 01: 库基础操作") +print("=" * 60) + +# 1. 初始化 +mtmlLibraryInit() +print("[1] 库初始化成功") + +# 2. 查询库版本 +version = mtmlLibraryGetVersion() +print(f"[2] MTML 库版本: {version}") + +# 3. 查询驱动版本 +system = mtmlLibraryInitSystem() +driver_ver = mtmlSystemGetDriverVersion(system) +print(f"[3] 驱动版本: {driver_ver}") +mtmlLibraryFreeSystem(system) + +# 4. 枚举设备 +device_count = mtmlLibraryCountDevice() +print(f"[4] 检测到 {device_count} 个 GPU 设备") + +for i in range(device_count): + device = mtmlLibraryInitDeviceByIndex(i) + name = mtmlDeviceGetName(device) + uuid = mtmlDeviceGetUUID(device) + print(f" 设备 {i}: {name} (UUID: {uuid})") + +# 5. 按 UUID 获取设备 +if device_count > 0: + device = mtmlLibraryInitDeviceByIndex(0) + uuid = mtmlDeviceGetUUID(device) + device_by_uuid = mtmlLibraryInitDeviceByUuid(uuid) + print(f"[5] 按 UUID 获取设备: {mtmlDeviceGetName(device_by_uuid)}") + +# 6. 按 PCI SBDF 获取设备 +if device_count > 0: + pci = mtmlDeviceGetPciInfo(mtmlLibraryInitDeviceByIndex(0)) + device_by_pci = mtmlLibraryInitDeviceByPciSbdf(pci.sbdf) + print(f"[6] 按 PCI SBDF ({pci.sbdf}) 获取设备: {mtmlDeviceGetName(device_by_pci)}") + +# 7. 关闭 +mtmlLibraryShutDown() +print("[7] 库关闭成功") + +# 8. 多次 init/shutdown 循环 +print("[8] 测试多次 init/shutdown 循环:") +for cycle in range(3): + mtmlLibraryInit() + count = mtmlLibraryCountDevice() + mtmlLibraryShutDown() + print(f" 第 {cycle + 1} 次循环: 检测到 {count} 个设备 ✓") + +print("\n完成!") diff --git a/examples/02_device_info.py b/examples/02_device_info.py new file mode 100644 index 0000000..8adb084 --- /dev/null +++ b/examples/02_device_info.py @@ -0,0 +1,63 @@ +""" +示例 02: 设备信息查询 +演示如何查询 GPU 设备的基本属性信息。 +""" + +from pymtml import * + +print("=" * 60) +print(" 示例 02: 设备信息查询") +print("=" * 60) + +mtmlLibraryInit() + +for i in range(mtmlLibraryCountDevice()): + device = mtmlLibraryInitDeviceByIndex(i) + + print(f"\n--- 设备 {i} ---") + print(f" 名称: {mtmlDeviceGetName(device)}") + print(f" 索引: {mtmlDeviceGetIndex(device)}") + print(f" UUID: {mtmlDeviceGetUUID(device)}") + print(f" 品牌: {'MTT' if mtmlDeviceGetBrand(device) == MTML_BRAND_MTT else 'Unknown'}") + + try: + print(f" 序列号: {mtmlDeviceGetSerialNumber(device)}") + except MTMLError as e: + print(f" 序列号: [不可用: {e}]") + + print(f" VBIOS 版本: {mtmlDeviceGetVbiosVersion(device)}") + + try: + print(f" MtBIOS 版本: {mtmlDeviceGetMtBiosVersion(device)}") + except MTMLError as e: + print(f" MtBIOS 版本: [不可用: {e}]") + + print(f" GPU 核心数: {mtmlDeviceCountGpuCores(device)}") + print(f" 功耗: {mtmlDeviceGetPowerUsage(device)} mW " + f"({mtmlDeviceGetPowerUsage(device) / 1000:.1f} W)") + + # PCI 信息 + pci = mtmlDeviceGetPciInfo(device) + print(f" PCI SBDF: {pci.sbdf}") + print(f" PCI Bus ID: {pci.busId}") + print(f" PCI 设备ID: {pci.pciDeviceId:#010x}") + print(f" PCIe 代数: 当前 Gen{pci.pciCurGen} / 最大 Gen{pci.pciMaxGen}") + print(f" PCIe 宽度: 当前 x{pci.pciCurWidth} / 最大 x{pci.pciMaxWidth}") + print(f" PCIe 速率: 当前 {pci.pciCurSpeed:.1f} GT/s / 最大 {pci.pciMaxSpeed:.1f} GT/s") + + # PCIe 插槽信息 + try: + slot = mtmlDeviceGetPcieSlotInfo(device) + print(f" PCIe 插槽: {slot.slotName} (type={slot.slotType})") + except MTMLError as e: + print(f" PCIe 插槽: [不可用: {e}]") + + # 设备属性 + prop = mtmlDeviceGetProperty(device) + virt_cap = "支持" if prop.virtCapability == MTML_DEVICE_SUPPORT_VIRTUALIZATION else "不支持" + mpc_cap = "支持" if prop.mpcCapability == MTML_DEVICE_SUPPORT_MPC else "不支持" + print(f" 虚拟化: {virt_cap}") + print(f" MPC: {mpc_cap}") + +mtmlLibraryShutDown() +print("\n完成!") diff --git a/examples/03_gpu_monitoring.py b/examples/03_gpu_monitoring.py new file mode 100644 index 0000000..d94e240 --- /dev/null +++ b/examples/03_gpu_monitoring.py @@ -0,0 +1,47 @@ +""" +示例 03: GPU 监控 +演示如何查询 GPU 利用率、温度、时钟频率及各引擎利用率。 +""" + +from pymtml import * + +print("=" * 60) +print(" 示例 03: GPU 监控") +print("=" * 60) + +mtmlLibraryInit() + +engine_names = { + MTML_GPU_ENGINE_GEOMETRY: "几何引擎", + MTML_GPU_ENGINE_2D: "2D 引擎", + MTML_GPU_ENGINE_3D: "3D 引擎", + MTML_GPU_ENGINE_COMPUTE: "计算引擎", +} + +for i in range(mtmlLibraryCountDevice()): + device = mtmlLibraryInitDeviceByIndex(i) + name = mtmlDeviceGetName(device) + print(f"\n--- 设备 {i}: {name} ---") + + with mtmlGpuContext(device) as gpu: + # 基本指标 + util = mtmlGpuGetUtilization(gpu) + temp = mtmlGpuGetTemperature(gpu) + clock = mtmlGpuGetClock(gpu) + max_clock = mtmlGpuGetMaxClock(gpu) + + print(f" GPU 利用率: {util}%") + print(f" GPU 温度: {temp}°C") + print(f" GPU 时钟: {clock} / {max_clock} MHz") + + # 各引擎利用率 + print(f" 引擎利用率:") + for engine_id, engine_name in engine_names.items(): + try: + engine_util = mtmlGpuGetEngineUtilization(gpu, engine_id) + print(f" {engine_name}: {engine_util}%") + except MTMLError as e: + print(f" {engine_name}: [不可用: {e}]") + +mtmlLibraryShutDown() +print("\n完成!") diff --git a/examples/04_memory_monitoring.py b/examples/04_memory_monitoring.py new file mode 100644 index 0000000..5b69fab --- /dev/null +++ b/examples/04_memory_monitoring.py @@ -0,0 +1,62 @@ +""" +示例 04: 显存监控 +演示如何查询显存使用情况、带宽、时钟等信息。 +""" + +from pymtml import * + +print("=" * 60) +print(" 示例 04: 显存监控") +print("=" * 60) + +mtmlLibraryInit() + +for i in range(mtmlLibraryCountDevice()): + device = mtmlLibraryInitDeviceByIndex(i) + name = mtmlDeviceGetName(device) + print(f"\n--- 设备 {i}: {name} ---") + + with mtmlMemoryContext(device) as memory: + total = mtmlMemoryGetTotal(memory) + used = mtmlMemoryGetUsed(memory) + free = total - used + util = mtmlMemoryGetUtilization(memory) + + print(f" 显存总量: {total / 1024**3:.2f} GB ({total / 1024**2:.0f} MB)") + print(f" 已用显存: {used / 1024**3:.2f} GB ({used / 1024**2:.0f} MB)") + print(f" 空闲显存: {free / 1024**3:.2f} GB ({free / 1024**2:.0f} MB)") + print(f" 显存利用率: {util}%") + + # 时钟 + clock = mtmlMemoryGetClock(memory) + max_clock = mtmlMemoryGetMaxClock(memory) + print(f" 显存时钟: {clock} / {max_clock} MHz") + + # 带宽和总线 + bus_width = mtmlMemoryGetBusWidth(memory) + bandwidth = mtmlMemoryGetBandwidth(memory) + speed = mtmlMemoryGetSpeed(memory) + print(f" 总线宽度: {bus_width} bits") + print(f" 显存带宽: {bandwidth} GB/s") + print(f" 显存速率: {speed} Mbps") + + # 类型和供应商 + mem_type = mtmlMemoryGetType(memory) + type_names = {MTML_MEM_TYPE_LPDDR4: "LPDDR4", MTML_MEM_TYPE_GDDR6: "GDDR6"} + print(f" 显存类型: {type_names.get(mem_type, f'Unknown({mem_type})')}") + + try: + vendor = mtmlMemoryGetVendor(memory) + print(f" 显存供应商: {vendor}") + except MTMLError as e: + print(f" 显存供应商: [不可用: {e}]") + + # 系统使用量 + try: + sys_used = mtmlMemoryGetUsedSystem(memory) + print(f" 系统占用显存: {sys_used / 1024**2:.2f} MB") + except MTMLError as e: + print(f" 系统占用显存: [不可用: {e}]") + +mtmlLibraryShutDown() +print("\n完成!") diff --git a/examples/05_vpu_monitoring.py b/examples/05_vpu_monitoring.py new file mode 100644 index 0000000..2bf8924 --- /dev/null +++ b/examples/05_vpu_monitoring.py @@ -0,0 +1,68 @@ +""" +示例 05: VPU (视频处理单元) 监控 +演示如何查询 VPU 时钟、编解码利用率和容量信息。 +""" + +from pymtml import * + +print("=" * 60) +print(" 示例 05: VPU 监控") +print("=" * 60) + +mtmlLibraryInit() + +for i in range(mtmlLibraryCountDevice()): + device = mtmlLibraryInitDeviceByIndex(i) + name = mtmlDeviceGetName(device) + print(f"\n--- 设备 {i}: {name} ---") + + try: + with mtmlVpuContext(device) as vpu: + # 时钟 + clock = mtmlVpuGetClock(vpu) + max_clock = mtmlVpuGetMaxClock(vpu) + print(f" VPU 时钟: {clock} / {max_clock} MHz") + + # 编解码利用率 + util = mtmlVpuGetUtilization(vpu) + print(f" 编码利用率: {util.encodeUtil}%") + print(f" 解码利用率: {util.decodeUtil}%") + + # 编解码容量 + enc_cap, dec_cap = mtmlVpuGetCodecCapacity(vpu) + print(f" 编码容量: {enc_cap}") + print(f" 解码容量: {dec_cap}") + + # 编码器会话状态 + try: + enc_states = mtmlVpuGetEncoderSessionStates(vpu, 8) + active_enc = sum(1 for s in enc_states if s.state == MTML_CODEC_SESSION_STATE_ACTIVE) + print(f" 活跃编码会话: {active_enc}") + for s in enc_states: + if s.state == MTML_CODEC_SESSION_STATE_ACTIVE: + metrics = mtmlVpuGetEncoderSessionMetrics(vpu, s.sessionId) + print(f" 编码会话 {s.sessionId}: {metrics.width}x{metrics.height}, " + f"codec={metrics.codecType}, fps={metrics.fps}") + break + except MTMLError: + print(f" 活跃编码会话: [不可用]") + + # 解码器会话状态 + try: + dec_states = mtmlVpuGetDecoderSessionStates(vpu, 8) + active_dec = sum(1 for s in dec_states if s.state == MTML_CODEC_SESSION_STATE_ACTIVE) + print(f" 活跃解码会话: {active_dec}") + for s in dec_states: + if s.state == MTML_CODEC_SESSION_STATE_ACTIVE: + metrics = mtmlVpuGetDecoderSessionMetrics(vpu, s.sessionId) + print(f" 解码会话 {s.sessionId}: {metrics.width}x{metrics.height}, " + f"codec={metrics.codecType}, fps={metrics.fps}") + break + except MTMLError: + print(f" 活跃解码会话: [不可用]") + + except MTMLError as e: + print(f" VPU 不可用: {e}") + +mtmlLibraryShutDown() +print("\n完成!") diff --git a/examples/06_fan_and_power.py b/examples/06_fan_and_power.py new file mode 100644 index 0000000..1e3c0a5 --- /dev/null +++ b/examples/06_fan_and_power.py @@ -0,0 +1,44 @@ +""" +示例 06: 风扇与功耗监控 +演示如何查询风扇转速和设备功耗。 +""" + +from pymtml import * + +print("=" * 60) +print(" 示例 06: 风扇与功耗监控") +print("=" * 60) + +mtmlLibraryInit() + +for i in range(mtmlLibraryCountDevice()): + device = mtmlLibraryInitDeviceByIndex(i) + name = mtmlDeviceGetName(device) + print(f"\n--- 设备 {i}: {name} ---") + + # 功耗 + power_mw = mtmlDeviceGetPowerUsage(device) + print(f" 当前功耗: {power_mw} mW ({power_mw / 1000:.1f} W)") + + # 风扇 + try: + fan_count = mtmlDeviceCountFan(device) + print(f" 风扇数量: {fan_count}") + + for f in range(fan_count): + try: + speed = mtmlDeviceGetFanSpeed(device, f) + print(f" 风扇 {f} 转速: {speed}%") + except MTMLError as e: + print(f" 风扇 {f} 转速: [不可用: {e}]") + + try: + rpm = mtmlDeviceGetFanRpm(device, f) + print(f" 风扇 {f} RPM: {rpm}") + except MTMLError as e: + print(f" 风扇 {f} RPM: [不可用: {e}]") + except MTMLError as e: + print(f" 风扇信息: [不可用: {e}]") + +mtmlLibraryShutDown() +print("\n完成!") diff --git a/examples/07_ecc_errors.py b/examples/07_ecc_errors.py new file mode 100644 index 0000000..9ddb629 --- /dev/null +++ b/examples/07_ecc_errors.py @@ -0,0 +1,93 @@ +""" +示例 07: ECC 错误查询 +演示如何查询 ECC 模式和错误计数。 +""" + +from pymtml import * + +print("=" * 60) +print(" 示例 07: ECC 错误查询") +print("=" * 60) + +mtmlLibraryInit() + +for i in range(mtmlLibraryCountDevice()): + device = mtmlLibraryInitDeviceByIndex(i) + name = mtmlDeviceGetName(device) + print(f"\n--- 设备 {i}: {name} ---") + + with mtmlMemoryContext(device) as memory: + # ECC 模式 + try: + current_mode, pending_mode = mtmlMemoryGetEccMode(memory) + print(f" ECC 当前模式: {'启用' if current_mode else '禁用'}") + print(f" ECC 待定模式: {'启用' if pending_mode else '禁用'}") + except MTMLError as e: + print(f" ECC 模式: [不可用: {e}]") + + # 已纠正错误 (Volatile) + try: + corrected_vol = mtmlMemoryGetEccErrorCounter( + memory, MTML_MEMORY_ERROR_TYPE_CORRECTED, + MTML_VOLATILE_ECC, MTML_MEMORY_LOCATION_DRAM) + print(f" 已纠正错误 (当前会话): {corrected_vol}") + except MTMLError as e: + print(f" 已纠正错误 (当前会话): [不可用: {e}]") + + # 未纠正错误 (Volatile) + try: + uncorrected_vol = mtmlMemoryGetEccErrorCounter( + memory, MTML_MEMORY_ERROR_TYPE_UNCORRECTED, + MTML_VOLATILE_ECC, MTML_MEMORY_LOCATION_DRAM) + print(f" 未纠正错误 (当前会话): {uncorrected_vol}") + except MTMLError as e: + print(f" 未纠正错误 (当前会话): [不可用: {e}]") + + # 已纠正错误 (Aggregate) + try: + corrected_agg = mtmlMemoryGetEccErrorCounter( + memory, MTML_MEMORY_ERROR_TYPE_CORRECTED, + MTML_AGGREGATE_ECC, MTML_MEMORY_LOCATION_DRAM) + print(f" 已纠正错误 (累计): {corrected_agg}") + except MTMLError as e: + print(f" 已纠正错误 (累计): [不可用: {e}]") + + # 未纠正错误 (Aggregate) + try: + uncorrected_agg = mtmlMemoryGetEccErrorCounter( + memory, MTML_MEMORY_ERROR_TYPE_UNCORRECTED, + MTML_AGGREGATE_ECC, MTML_MEMORY_LOCATION_DRAM) + print(f" 未纠正错误 (累计): {uncorrected_agg}") + except MTMLError as e: + print(f" 未纠正错误 (累计): [不可用: {e}]") + + # 退役页面 + try: + retired = mtmlMemoryGetRetiredPagesCount(memory) + print(f" 退役页面 (单比特ECC): {retired.singleBitEcc}") + print(f" 退役页面 (双比特ECC): {retired.doubleBitEcc}") + except MTMLError as e: + print(f" 退役页面: [不可用: {e}]") + + try: + pending = mtmlMemoryGetRetiredPagesPendingStatus(memory) + print(f" 退役页面挂起: {'是' if pending else '否'}") + except MTMLError as e: + print(f" 退役页面挂起: [不可用: {e}]") + try: + pages_sb = mtmlMemoryGetRetiredPages( + memory, MTML_PAGE_RETIREMENT_CAUSE_MULTIPLE_SINGLE_BIT_ECC_ERRORS, 0 + ) + print(" 退役页面列表 (单比特ECC):", len(pages_sb), "条") + except MTMLError as e: + print(" 退役页面列表 (单比特ECC): [不可用]", e) + try: + pages_db = mtmlMemoryGetRetiredPages( + memory, MTML_PAGE_RETIREMENT_CAUSE_DOUBLE_BIT_ECC_ERROR, 0 + ) + print(" 退役页面列表 (双比特ECC):", len(pages_db), "条") + except MTMLError as e: + print(" 退役页面列表 (双比特ECC): [不可用]", e) + +mtmlLibraryShutDown() +print("\n完成!") diff --git a/examples/08_topology_and_mtlink.py b/examples/08_topology_and_mtlink.py new file mode 100644 index 0000000..42f42e3 --- /dev/null +++ b/examples/08_topology_and_mtlink.py @@ -0,0 +1,143 @@ +""" +示例 08: 拓扑与 MtLink 互连 +演示如何查询多 GPU 拓扑关系和 MtLink 互连状态。 +注意: 拓扑 API 需要 2 个以上 GPU 才能展示完整功能。 +""" + +from pymtml import * + +print("=" * 60) +print(" 示例 08: 拓扑与 MtLink 互连") +print("=" * 60) + +mtmlLibraryInit() + +device_count = mtmlLibraryCountDevice() +print(f"\n检测到 {device_count} 个 GPU 设备") + +devices = [] +for i in range(device_count): + device = mtmlLibraryInitDeviceByIndex(i) + devices.append(device) + name = mtmlDeviceGetName(device) + print(f" 设备 {i}: {name}") + +# MtLink 信息 +print(f"\n--- MtLink 信息 ---") +mtlink_state_names = { + MTML_MTLINK_STATE_DOWN: "断开", + MTML_MTLINK_STATE_UP: "连接", + MTML_MTLINK_STATE_DOWNGRADE: "降级", +} + +for i, device in enumerate(devices): + try: + spec = mtmlDeviceGetMtLinkSpec(device) + print(f"\n 设备 {i} MtLink 规格:") + print(f" 版本: {spec.version}") + print(f" 带宽: {spec.bandWidth}") + print(f" 链路数: {spec.linkNum}") + + for link in range(spec.linkNum): + try: + state = mtmlDeviceGetMtLinkState(device, link) + state_str = mtlink_state_names.get(state, f"未知({state})") + print(f" 链路 {link}: {state_str}", end="") + + if state == MTML_MTLINK_STATE_UP: + try: + remote = mtmlDeviceGetMtLinkRemoteDevice(device, link) + remote_name = mtmlDeviceGetName(remote) + print(f" -> {remote_name}") + except MTMLError: + print() + else: + print() + # 链路能力状态 (capability 通常为 0) + try: + cap_status = mtmlDeviceGetMtLinkCapStatus(device, link, 0) + print(f" MtLinkCapStatus(link={link}, cap=0): {cap_status}") + except MTMLError: + pass + except MTMLError as e: + print(f" 链路 {link}: [不可用: {e}]") + except MTMLError as e: + print(f"\n 设备 {i}: MtLink 不可用 ({e})") + +# 拓扑关系 (需要 2+ GPU) +if device_count >= 2: + topo_names = { + MTML_TOPOLOGY_INTERNAL: "同一GPU", + MTML_TOPOLOGY_SINGLE: "单PCIe交换机", + MTML_TOPOLOGY_MULTIPLE: "多PCIe交换机", + MTML_TOPOLOGY_HOSTBRIDGE: "主桥", + MTML_TOPOLOGY_NODE: "同一NUMA节点", + MTML_TOPOLOGY_SYSTEM: "跨NUMA节点", + } + + print(f"\n--- 拓扑关系矩阵 ---") + for i in range(device_count): + for j in range(i + 1, device_count): + try: + level = mtmlDeviceGetTopologyLevel(devices[i], devices[j]) + level_name = topo_names.get(level, f"未知({level})") + print(f" 设备 {i} <-> 设备 {j}: {level_name}") + except MTMLError as e: + print(f" 设备 {i} <-> 设备 {j}: [不可用: {e}]") + + # P2P 状态 + print(f"\n--- P2P 状态 ---") + for i in range(device_count): + for j in range(i + 1, device_count): + try: + read_status = mtmlDeviceGetP2PStatus(devices[i], devices[j], MTML_P2P_CAPS_READ) + write_status = mtmlDeviceGetP2PStatus(devices[i], devices[j], MTML_P2P_CAPS_WRITE) + read_ok = "OK" if read_status == MTML_P2P_STATUS_OK else f"Status={read_status}" + write_ok = "OK" if write_status == MTML_P2P_STATUS_OK else f"Status={write_status}" + print(f" 设备 {i} <-> 设备 {j}: 读={read_ok}, 写={write_ok}") + except MTMLError as e: + print(f" 设备 {i} <-> 设备 {j}: [不可用: {e}]") + + # MtLink 布局 + print(f"\n--- MtLink 布局 ---") + for i in range(device_count): + for j in range(i + 1, device_count): + try: + link_count = mtmlDeviceCountMtLinkLayouts(devices[i], devices[j]) + print(f" 设备 {i} <-> 设备 {j}: {link_count} 条链路") + if link_count > 0: + layouts = mtmlDeviceGetMtLinkLayouts(devices[i], devices[j], link_count) + for layout in layouts: + print(f" 本地链路ID={layout.localLinkId} <-> 远程链路ID={layout.remoteLinkId}") + except MTMLError as e: + print(f" 设备 {i} <-> 设备 {j}: [不可用: {e}]") + + # MtLink 最短路径 (两设备间) + print(f"\n--- MtLink 最短路径 ---") + try: + path_count, path_length = mtmlDeviceCountMtLinkShortestPaths(devices[0], devices[1]) + print(f" 设备 0 -> 设备 1: {path_count} 条路径, 长度 {path_length}") + if path_count > 0 and path_length > 0: + paths = mtmlDeviceGetMtLinkShortestPaths( + devices[0], devices[1], path_count, path_length + ) + for idx, path in enumerate(paths): + print(f" 路径 {idx}: {len(path)} 跳") + except MTMLError as e: + print(f" [不可用: {e}]") + + # 按拓扑层级统计设备 / 获取同层级设备 + print(f"\n--- 按拓扑层级设备 ---") + for level in range(MTML_TOPOLOGY_SYSTEM + 1): + try: + count = mtmlDeviceCountDeviceByTopologyLevel(devices[0], level) + if count > 0: + level_devs = mtmlDeviceGetDeviceByTopologyLevel(devices[0], level, count) + print(f" 层级 {level}: {count} 个设备 (GetDeviceByTopologyLevel)") + except MTMLError: + pass +else: + print("\n[提示] 拓扑和 P2P 测试需要 2 个以上 GPU 设备") + +mtmlLibraryShutDown() +print("\n完成!") diff --git a/examples/09_nvml_compatibility.py b/examples/09_nvml_compatibility.py new file mode 100644 index 0000000..caa2f9d --- /dev/null +++ b/examples/09_nvml_compatibility.py @@ -0,0 +1,146 @@ +""" +示例 09: NVML 兼容层 +演示如何使用 'import pymtml as pynvml' 替代 pynvml, +实现对现有 NVML 代码的无缝迁移。 +""" + +import pymtml as pynvml + +print("=" * 60) +print(" 示例 09: NVML 兼容层 (import pymtml as pynvml)") +print("=" * 60) + +# 初始化 (NVML 风格) +pynvml.nvmlInit() +print("[1] nvmlInit() 成功") + +# 系统信息 +driver_ver = pynvml.nvmlSystemGetDriverVersion() +print(f"[2] 驱动版本: {driver_ver}") + +# 设备枚举 +device_count = pynvml.nvmlDeviceGetCount() +print(f"[3] GPU 数量: {device_count}") + +for i in range(device_count): + handle = pynvml.nvmlDeviceGetHandleByIndex(i) + print(f"\n--- GPU {i} ---") + + # 基本信息 + name = pynvml.nvmlDeviceGetName(handle) + uuid = pynvml.nvmlDeviceGetUUID(handle) + index = pynvml.nvmlDeviceGetIndex(handle) + print(f" 名称: {name}") + print(f" UUID: {uuid}") + print(f" 索引: {index}") + + # PCI 信息 + pci = pynvml.nvmlDeviceGetPciInfo(handle) + print(f" PCI SBDF: {pci.sbdf}") + print(f" PCI BusId: {pci.busId}") + + # 显存信息 (返回 NVMLMemoryInfo dataclass) + mem = pynvml.nvmlDeviceGetMemoryInfo(handle) + print(f" 显存总量: {mem.total / 1024**3:.2f} GB") + print(f" 已用显存: {mem.used / 1024**3:.2f} GB") + print(f" 空闲显存: {mem.free / 1024**3:.2f} GB") + + # 利用率 (返回 NVMLUtilization dataclass) + util = pynvml.nvmlDeviceGetUtilizationRates(handle) + print(f" GPU 利用率: {util.gpu}%") + print(f" 显存利用率: {util.memory}%") + + # 温度 + temp = pynvml.nvmlDeviceGetTemperature(handle, pynvml.NVML_TEMPERATURE_GPU) + print(f" 温度: {temp}°C") + + # 功耗 + power = pynvml.nvmlDeviceGetPowerUsage(handle) + print(f" 功耗: {power / 1000:.1f} W") + + # 时钟频率 + gpu_clock = pynvml.nvmlDeviceGetClockInfo(handle, pynvml.NVML_CLOCK_GRAPHICS) + mem_clock = pynvml.nvmlDeviceGetClockInfo(handle, pynvml.NVML_CLOCK_MEM) + vid_clock = pynvml.nvmlDeviceGetClockInfo(handle, pynvml.NVML_CLOCK_VIDEO) + print(f" GPU 时钟: {gpu_clock} MHz") + print(f" 显存时钟: {mem_clock} MHz") + print(f" 视频时钟: {vid_clock} MHz") + + gpu_max = pynvml.nvmlDeviceGetMaxClockInfo(handle, pynvml.NVML_CLOCK_GRAPHICS) + mem_max = pynvml.nvmlDeviceGetMaxClockInfo(handle, pynvml.NVML_CLOCK_MEM) + print(f" GPU 最大时钟: {gpu_max} MHz") + print(f" 显存最大时钟: {mem_max} MHz") + + # 风扇 + fan = pynvml.nvmlDeviceGetFanSpeed(handle) + print(f" 风扇转速: {fan}%") + + # 编解码器 + enc = pynvml.nvmlDeviceGetEncoderUtilization(handle) + dec = pynvml.nvmlDeviceGetDecoderUtilization(handle) + print(f" 编码器利用率: {enc[0]}%") + print(f" 解码器利用率: {dec[0]}%") + + # 其他 NVML 兼容接口 + print(f" GPU 核心数: {pynvml.nvmlDeviceGetNumGpuCores(handle)}") + print(f" 显存总线宽度: {pynvml.nvmlDeviceGetMemoryBusWidth(handle)} bits") + print(f" VBIOS 版本: {pynvml.nvmlDeviceGetVbiosVersion(handle)}") + print(f" Minor Number: {pynvml.nvmlDeviceGetMinorNumber(handle)}") + + # ECC + ecc_current, ecc_pending = pynvml.nvmlDeviceGetEccMode(handle) + print(f" ECC 模式: 当前={'启用' if ecc_current else '禁用'}, " + f"待定={'启用' if ecc_pending else '禁用'}") + + # MUSA 计算能力 (需要 torch_musa) + major, minor = pynvml.nvmlDeviceGetCudaComputeCapability(handle) + if major > 0: + print(f" MUSA 计算能力: {major}.{minor}") + else: + print(f" MUSA 计算能力: 不可用 (需要 torch_musa)") + + # NVLink/MtLink 状态 (链路 0) + try: + nvlink_state = pynvml.nvmlDeviceGetNvLinkState(handle, 0) + print(f" NvLink/MtLink 链路0: {'UP' if nvlink_state else 'DOWN'}") + except pynvml.NVMLError: + print(f" NvLink/MtLink 链路0: [不可用]") + try: + cap = pynvml.nvmlDeviceGetNvLinkCapability(handle, 0, 0) + print(f" NvLink/MtLink 链路0 能力(0): {cap}") + except pynvml.NVMLError: + pass + try: + remote_pci = pynvml.nvmlDeviceGetNvLinkRemotePciInfo(handle, 0) + if remote_pci: + print(f" NvLink/MtLink 链路0 远端 PCI: {remote_pci.busId}") + except pynvml.NVMLError: + pass + +# 多 GPU 时: P2P 与拓扑 (NVML 接口) +if device_count >= 2: + print(f"\n--- 多 GPU: P2P 与拓扑 (NVML) ---") + h0 = pynvml.nvmlDeviceGetHandleByIndex(0) + h1 = pynvml.nvmlDeviceGetHandleByIndex(1) + try: + p2p_read = pynvml.nvmlDeviceGetP2PStatus(h0, h1, pynvml.NVML_P2P_CAPS_INDEX_READ) + p2p_write = pynvml.nvmlDeviceGetP2PStatus(h0, h1, pynvml.NVML_P2P_CAPS_INDEX_WRITE) + p2p_nvlink = pynvml.nvmlDeviceGetP2PStatus(h0, h1, pynvml.NVML_P2P_CAPS_INDEX_NVLINK) + print(f" P2P 读: {p2p_read}, 写: {p2p_write}, NvLink/MtLink: {p2p_nvlink}") + except pynvml.NVMLError as e: + print(f" P2P: [不可用: {e}]") + try: + topo_ancestor = pynvml.nvmlDeviceGetTopologyCommonAncestor(h0, h1) + print(f" 拓扑公共祖先层级: {topo_ancestor}") + except pynvml.NVMLError as e: + print(f" 拓扑公共祖先: [不可用: {e}]") + try: + nearest = pynvml.nvmlDeviceGetTopologyNearestGpus(h0, pynvml.NVML_TOPOLOGY_NODE) + print(f" 同 NUMA 节点邻近 GPU 数: {len(nearest)}") + except pynvml.NVMLError as e: + print(f" 拓扑邻近 GPU: [不可用: {e}]") + +# 关闭 +pynvml.nvmlShutdown() +print(f"\n[4] nvmlShutdown() 成功") +print("\n完成!") diff --git a/examples/10_device_paths.py b/examples/10_device_paths.py new file mode 100644 index 0000000..7b6ab7a --- /dev/null +++ b/examples/10_device_paths.py @@ -0,0 +1,48 @@ +""" +示例 10: 设备路径与显示接口 +演示如何查询 GPU、Primary、Render 设备路径及显示接口信息。 +""" + +from pymtml import * + +print("=" * 60) +print(" 示例 10: 设备路径与显示接口") +print("=" * 60) + +mtmlLibraryInit() + +for i in range(mtmlLibraryCountDevice()): + device = mtmlLibraryInitDeviceByIndex(i) + name = mtmlDeviceGetName(device) + print(f"\n--- 设备 {i}: {name} ---") + + # 设备路径 + print(f" GPU 路径: {mtmlDeviceGetGpuPath(device)}") + print(f" Primary 路径: {mtmlDeviceGetPrimaryPath(device)}") + print(f" Render 路径: {mtmlDeviceGetRenderPath(device)}") + + # 显示接口 + disp_type_names = { + MTML_DISP_INTF_TYPE_DP: "DisplayPort", + MTML_DISP_INTF_TYPE_EDP: "eDP", + MTML_DISP_INTF_TYPE_VGA: "VGA", + MTML_DISP_INTF_TYPE_HDMI: "HDMI", + MTML_DISP_INTF_TYPE_LVDS: "LVDS", + } + + try: + disp_count = mtmlDeviceCountDisplayInterface(device) + print(f" 显示接口数量: {disp_count}") + for d in range(disp_count): + try: + spec = mtmlDeviceGetDisplayInterfaceSpec(device, d) + type_name = disp_type_names.get(spec.type, f"Unknown({spec.type})") + print(f" 接口 {d}: {type_name}, " + f"最大分辨率 {spec.maxResWidth}x{spec.maxResHeight}") + except MTMLError as e: + print(f" 接口 {d}: [不可用: {e}]") + except MTMLError as e: + print(f" 显示接口: [不可用: {e}]") + +mtmlLibraryShutDown() +print("\n完成!") diff --git a/examples/11_mpc_and_virtualization.py b/examples/11_mpc_and_virtualization.py new file mode 100644 index 0000000..1699863 --- /dev/null +++ b/examples/11_mpc_and_virtualization.py @@ -0,0 +1,97 @@ +""" +示例 11: MPC 与虚拟化 +演示如何查询 MPC (Multi-GPU Partitioned Computing) 配置 +和虚拟化相关信息。 +""" + +from pymtml import * + +print("=" * 60) +print(" 示例 11: MPC 与虚拟化") +print("=" * 60) + +mtmlLibraryInit() + +for i in range(mtmlLibraryCountDevice()): + device = mtmlLibraryInitDeviceByIndex(i) + name = mtmlDeviceGetName(device) + print(f"\n--- 设备 {i}: {name} ---") + + # 设备属性 + prop = mtmlDeviceGetProperty(device) + + # MPC 信息 + mpc_cap = prop.mpcCapability == MTML_DEVICE_SUPPORT_MPC + mpc_type_names = { + MTML_MPC_TYPE_NONE: "无", + MTML_MPC_TYPE_PARENT: "父设备", + MTML_MPC_TYPE_INSTANCE: "MPC 实例", + } + print(f" MPC 支持: {'是' if mpc_cap else '否'}") + print(f" MPC 类型: {mpc_type_names.get(prop.mpcType, f'Unknown({prop.mpcType})')}") + + try: + mode = mtmlDeviceGetMpcMode(device) + print(f" MPC 模式: {'启用' if mode else '禁用'}") + except MTMLError as e: + print(f" MPC 模式: [不可用: {e}]") + + try: + profile_count = mtmlDeviceCountSupportedMpcProfiles(device) + print(f" MPC 配置文件: {profile_count} 个") + if profile_count > 0: + profiles = mtmlDeviceGetSupportedMpcProfiles(device, profile_count) + for p in profiles: + print(f" ID={p.profileId}, 名称={p.name}, " + f"显存={p.memSize / 1024**3:.1f}GB, 核心数={p.gpuCores}") + except MTMLError as e: + print(f" MPC 配置文件: [不可用: {e}]") + + try: + config_count = mtmlDeviceCountSupportedMpcConfigurations(device) + print(f" MPC 配置方案: {config_count} 个") + except MTMLError as e: + print(f" MPC 配置方案: [不可用: {e}]") + + try: + instance_count = mtmlDeviceCountMpcInstances(device) + print(f" MPC 实例数: {instance_count}") + except MTMLError as e: + print(f" MPC 实例数: [不可用: {e}]") + + # 虚拟化信息 + virt_cap = prop.virtCapability == MTML_DEVICE_SUPPORT_VIRTUALIZATION + virt_role_names = { + MTML_VIRT_ROLE_NONE: "无", + MTML_VIRT_ROLE_HOST_VIRTDEVICE: "宿主机虚拟设备", + MTML_VIRT_ROLE_GUEST_VIRTDEVICE: "虚拟机虚拟设备", + } + print(f"\n 虚拟化支持: {'是' if virt_cap else '否'}") + print(f" 虚拟化角色: {virt_role_names.get(prop.virtRole, f'Unknown({prop.virtRole})')}") + + try: + supported = mtmlDeviceCountSupportedVirtTypes(device) + print(f" 支持的虚拟化类型: {supported} 个") + if supported > 0: + virt_types = mtmlDeviceGetSupportedVirtTypes(device, supported) + for vt in virt_types: + print(f" ID={vt.id}, 名称={vt.name}, 类别={vt.deviceClass}") + print(f" 最大实例数={vt.maxInstances}, 显存={vt.memSize / 1024**3:.1f}GB, " + f"核心数={vt.gpuCores}") + except MTMLError as e: + print(f" 支持的虚拟化类型: [不可用: {e}]") + + try: + avail = mtmlDeviceCountAvailVirtTypes(device) + print(f" 可用的虚拟化类型: {avail} 个") + except MTMLError as e: + print(f" 可用的虚拟化类型: [不可用: {e}]") + + try: + active = mtmlDeviceCountActiveVirtDevices(device) + print(f" 活跃虚拟设备: {active} 个") + except MTMLError as e: + print(f" 活跃虚拟设备: [不可用: {e}]") + +mtmlLibraryShutDown() +print("\n完成!") diff --git a/examples/12_affinity_and_log.py b/examples/12_affinity_and_log.py new file mode 100644 index 0000000..cb72715 --- /dev/null +++ b/examples/12_affinity_and_log.py @@ -0,0 +1,70 @@ +""" +示例 12: CPU 亲和性与日志配置 +演示如何查询 GPU 的 CPU/内存 NUMA 亲和性,以及获取日志配置。 +""" + +from pymtml import * + +print("=" * 60) +print(" 示例 12: CPU 亲和性与日志配置") +print("=" * 60) + +mtmlLibraryInit() + +for i in range(mtmlLibraryCountDevice()): + device = mtmlLibraryInitDeviceByIndex(i) + name = mtmlDeviceGetName(device) + print(f"\n--- 设备 {i}: {name} ---") + + # CPU 亲和性 + try: + cpu_set = mtmlDeviceGetCpuAffinityWithinNode(device, 4) + print(f" CPU 亲和性掩码: {[hex(x) for x in cpu_set]}") + + # 解析 CPU 核心列表 + cores = [] + for word_idx, word in enumerate(cpu_set): + for bit in range(64): + if word & (1 << bit): + cores.append(word_idx * 64 + bit) + if cores: + if len(cores) > 16: + print(f" CPU 核心列表: {cores[:8]}...{cores[-8:]} (共 {len(cores)} 核心)") + else: + print(f" CPU 核心列表: {cores}") + except MTMLError as e: + print(f" CPU 亲和性: [不可用: {e}]") + + # 内存亲和性 + try: + mem_set = mtmlDeviceGetMemoryAffinityWithinNode(device, 4) + print(f" 内存亲和性掩码: {[hex(x) for x in mem_set]}") + + nodes = [] + for word_idx, word in enumerate(mem_set): + for bit in range(64): + if word & (1 << bit): + nodes.append(word_idx * 64 + bit) + print(f" NUMA 节点: {nodes}") + except MTMLError as e: + print(f" 内存亲和性: [不可用: {e}]") + +# 日志配置 +print(f"\n--- 日志配置 ---") +try: + log_config = mtmlLogGetConfiguration() + log_level_names = { + MTML_LOG_LEVEL_OFF: "关闭", + MTML_LOG_LEVEL_FATAL: "致命", + MTML_LOG_LEVEL_ERROR: "错误", + MTML_LOG_LEVEL_WARNING: "警告", + MTML_LOG_LEVEL_INFO: "信息", + } + print(f" 日志文件: {log_config.filePath}") + print(f" 最大大小: {log_config.maxSize}") + print(f" 日志级别: {log_level_names.get(log_config.logLevel, f'Unknown({log_config.logLevel})')}") +except MTMLError as e: + print(f" [不可用: {e}]") + +mtmlLibraryShutDown() +print("\n完成!") diff --git a/examples/13_comprehensive_report.py b/examples/13_comprehensive_report.py new file mode 100644 index 0000000..13d1a22 --- /dev/null +++ b/examples/13_comprehensive_report.py @@ -0,0 +1,118 @@ +""" +示例 13: 综合 GPU 报告 +类似 nvidia-smi 的综合信息展示,一次性输出所有关键指标。 + +兼容性说明: +- 本示例针对 MTML >= 2.2.0 设计,但会显式处理旧版本运行库的兼容性。 +- 若用户无法升级到最新 MTML,部分 API 可能不可用;不可用项会显示为 N/A, + 而不会导致脚本报错,便于在旧环境下仍能查看可用信息。 +""" + +from pymtml import * + + +def _safe(fn, default=None, *args, **kwargs): + """调用可能在新版 MTML 才提供的 API;若不存在或失败则返回 default。""" + try: + return fn(*args, **kwargs) + except MTMLError: + return default + + +print("=" * 72) +print(" MTML GPU 综合报告") +print("=" * 72) + +mtmlLibraryInit() + +# 系统信息(版本/驱动在旧 MTML 上可能不可用) +version = _safe(mtmlLibraryGetVersion, "unknown") +driver_ver = "N/A" +system = _safe(mtmlLibraryInitSystem) +if system is not None: + try: + driver_ver = mtmlSystemGetDriverVersion(system) + except MTMLError: + pass + _safe(mtmlLibraryFreeSystem, None, system) + +device_count = mtmlLibraryCountDevice() +if version == "unknown": + print(" MTML 版本: (运行时可能 < 2.2.0,部分项将显示 N/A)") +else: + print(" MTML 版本: {} (目标 API 2.2.0+,旧运行库下部分项为 N/A)".format(version)) +print(" 驱动版本: {} GPU 数量: {}".format(driver_ver, device_count)) +print("=" * 72) + +for i in range(device_count): + device = mtmlLibraryInitDeviceByIndex(i) + name = mtmlDeviceGetName(device) + uuid = mtmlDeviceGetUUID(device) + + power = _safe(mtmlDeviceGetPowerUsage, None, device) + power_str = "{:.1f} W".format(power / 1000) if power is not None else "N/A" + + gpu_util = gpu_temp = gpu_clock = gpu_max_clock = None + gpu_ctx = _safe(mtmlGpuContext, None, device) + if gpu_ctx is not None: + try: + with gpu_ctx as gpu: + gpu_util = _safe(mtmlGpuGetUtilization, None, gpu) + gpu_temp = _safe(mtmlGpuGetTemperature, None, gpu) + gpu_clock = _safe(mtmlGpuGetClock, None, gpu) + gpu_max_clock = _safe(mtmlGpuGetMaxClock, None, gpu) + except MTMLError: + pass + gpu_util_str = "{}%".format(gpu_util) if gpu_util is not None else "N/A" + gpu_temp_str = "{}°C".format(gpu_temp) if gpu_temp is not None else "N/A" + gpu_clock_str = "{}/{} MHz".format(gpu_clock, gpu_max_clock) if (gpu_clock is not None and gpu_max_clock is not None) else "N/A" + + mem_total = mem_used = mem_util = mem_clock = mem_max_clock = None + mem_ctx = _safe(mtmlMemoryContext, None, device) + if mem_ctx is not None: + try: + with mem_ctx as memory: + mem_total = mtmlMemoryGetTotal(memory) + mem_used = mtmlMemoryGetUsed(memory) + mem_util = _safe(mtmlMemoryGetUtilization, None, memory) + mem_clock = _safe(mtmlMemoryGetClock, None, memory) + mem_max_clock = _safe(mtmlMemoryGetMaxClock, None, memory) + except MTMLError: + pass + if mem_total is not None and mem_used is not None: + mem_str = "{:.2f}/{:.2f} GB".format(mem_used / 1024**3, mem_total / 1024**3) + if mem_util is not None: + mem_str += " ({}%)".format(mem_util) + mem_str += " 时钟 " + ( + "{}/{} MHz".format(mem_clock, mem_max_clock) if (mem_clock is not None and mem_max_clock is not None) else "N/A" + ) + else: + mem_str = "N/A" + + try: + fan_speed = mtmlDeviceGetFanSpeed(device, 0) + fan_str = "{}%".format(fan_speed) + except MTMLError: + fan_str = "N/A" + + pci = _safe(mtmlDeviceGetPciInfo, None, device) + if pci is not None: + pci_str = "{} (Gen{} x{})".format(pci.sbdf, pci.pciCurGen, pci.pciCurWidth) + else: + pci_str = "N/A" + + print(""" + GPU {}: {} + ├─ UUID: {} + ├─ PCI: {} + ├─ 功耗: {} + ├─ 温度: {} 风扇: {} + ├─ GPU: 利用率 {} 时钟 {} + └─ 显存: {}""".format( + i, name, uuid, pci_str, power_str, gpu_temp_str, fan_str, gpu_util_str, gpu_clock_str, mem_str + )) + +print() +print("=" * 72) + +mtmlLibraryShutDown() diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..0fb3ea6 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,38 @@ +# pymtml 使用示例 + +本目录包含 pymtml 各类 API 的使用示例,每个示例独立运行。 + +## 示例列表 + +| 文件 | 内容 | 说明 | +|------|------|------| +| `01_library_basics.py` | 库基础操作 | 初始化、版本查询、设备枚举、init/shutdown 循环 | +| `02_device_info.py` | 设备信息查询 | 名称、UUID、序列号、PCI、核心数、功耗等 | +| `03_gpu_monitoring.py` | GPU 监控 | 利用率、温度、时钟频率、引擎利用率 | +| `04_memory_monitoring.py` | 显存监控 | 显存用量、带宽、时钟、类型、供应商 | +| `05_vpu_monitoring.py` | VPU 监控 | 视频编解码利用率、容量、会话状态 | +| `06_fan_and_power.py` | 风扇与功耗 | 风扇转速/RPM、设备功耗 | +| `07_ecc_errors.py` | ECC 错误 | ECC 模式、纠正/未纠正错误计数、退役页面 | +| `08_topology_and_mtlink.py` | 拓扑与互连 | MtLink 状态、拓扑层级、P2P 状态(需多卡) | +| `09_nvml_compatibility.py` | NVML 兼容层 | 以 `import pymtml as pynvml` 方式使用全部 NVML API | +| `10_device_paths.py` | 设备路径 | GPU/Primary/Render 路径、显示接口 | +| `11_mpc_and_virtualization.py` | MPC 与虚拟化 | MPC 配置、虚拟化类型和状态 | +| `12_affinity_and_log.py` | 亲和性与日志 | CPU/内存 NUMA 亲和性、日志配置 | +| `13_comprehensive_report.py` | 综合报告 | 类似 nvidia-smi 的一站式信息汇总 | + +## 运行方式 + +```bash +# 运行单个示例 +python examples/01_library_basics.py + +# 运行所有示例 +for f in examples/[0-9]*.py; do echo "--- $f ---"; python "$f"; echo; done +``` + +## 环境要求 + +- **Python** 3.7+ +- **MTML** 2.2.0+ +- **硬件** 摩尔线程 GPU +- **驱动** 已安装摩尔线程 GPU 驱动程序