MindSpore 针对昇腾 Ascend 硬件提供了一套完整 Python 层硬件操作接口,包含设备管理、NPU 内存查询、Host‑Device 数据拷贝、流同步、异构 Offload、内存池控制等能力。在大模型训练 / 微调 / 推理场景,合理使用硬件接口,可以实现显存监控、手动张量搬移、多卡设备管理、内存卸载调优,解决 OOM、同步异常、内存泄漏等问题。本文梳理 MindSpore 昇腾硬件操作原理、实操代码、分布式场景用法、性能采集与故障案例,覆盖 “设备初始化→内存操作→流同步→异构卸载→问题定位” 全链路,适配 MindSpore 2.4、MindSpore‑Transformers 1.4+、CANN 8.5.0,Atlas 800I‑A2,openEuler 22.03 ARM64 环境。

一、硬件操作环境准备

MindSpore 硬件运行时mindspore.runtime封装昇腾 NPU 底层能力,大模型脚本执行前完成 CANN 与 MindSpore 环境校验。

# 1.配置CANN环境变量
echo "source /usr/local/Ascend/ascend‑toolkit/8.5.0/set_env.sh" >> ~/.bashrc
source ~/.bashrc

# 版本校验
npu‑smi info
python -c "import mindspore; print(mindspore.__version__)"

# 安装大模型组件
pip install mindspore‑transformers

工程目录结构

ms_hardware_demo
├── hardware_op_demo.py    # 硬件操作示例脚本
├── offload_demo.py        # 异构内存卸载示例
└── prof_hw.sh             # msprof性能采集脚本

二、核心步骤:MindSpore 昇腾硬件实操

2.1 设备管理:设置设备、获取设备信息

mindspore.set_device用于指定昇腾设备,替代旧版set_context设备字段,支持单卡、多卡分布式场景。

import mindspore as ms
from mindspore import runtime

# 设置昇腾设备0,大模型脚本最开头执行
ms.set_device("Ascend", device_id=0)

# 获取当前使用设备id
cur_dev_id = ms.get_device_id()
print(f"当前设备ID:{cur_dev_id}")

# 获取物理设备总内存、已占用内存
mem_info = runtime.get_memory_info(device_id=0)
print(f"设备总内存 {mem_info['total'] / 1024 / 1024 / 1024:.2f} GB")
print(f"当前张量占用内存 {mem_info['allocated'] / 1024 / 1024 / 1024:.2f} GB")
print(f"内存池托管内存 {mem_info['reserved'] / 1024 / 1024 / 1024:.2f} GB")

分布式场景:每个 rank 进程调用ms.set_device("Ascend", device_id=rank)绑定对应卡号。

2.2 张量 Host‑Device 内存拷贝

大模型场景经常需要手动完成主机内存与 NPU 设备内存之间的数据迁移,.copy_from_host()、.copy_to_host()完成双向拷贝。

import mindspore as ms
import numpy as np

# numpy主机数据
host_np = np.random.randn(8,128).astype(np.float16)

# host -> npu:主机拷贝至昇腾设备
dev_tensor = ms.Tensor(host_np)

# npu -> host:设备张量拷贝回主机numpy数组
back_np = dev_tensor.asnumpy()

# 图模式下显式拷贝接口(runtime)
dev_tensor2 = ms.Tensor(shape=(8,128), dtype=ms.float16)
dev_tensor2.copy_from_host(host_np)
host_out = dev_tensor2.copy_to_host()

2.3 流同步操作,等待硬件任务完成

昇腾 NPU 算子下发是异步,不主动等待会出现 “计算还未完成就读取结果”,导致数值异常。

import mindspore.runtime as mrt

# 获取当前默认stream,同步等待该流上所有任务执行完毕
mrt.synchronize_stream()
print("NPU流全部任务执行完成")

提示:PYNATIVE 模式框架会自动同步;GRAPH 模式大模型循环中,如果需要读取设备张量结果,建议增加synchronize_stream()。

2.4 内存释放与内存池清理

MindSpore 默认开启内存池,张量销毁不会立刻还给硬件,保留在内存池复用。调试时可以手动清空内存池,释放 NPU 显存。

import mindspore.runtime as mrt

# 删除python侧张量引用
dev_tensor = None

# 清空内存池,将内存归还NPU硬件
mrt.empty_cache(device_id=0)

# 清空后再次打印内存
mem_after = mrt.get_memory_info(0)
print(f"empty_cache后已占用:{mem_after['allocated'] / 1024/1024:.2f} MB")

2.5 大模型异构 Offload 硬件配置(显存不足场景)

当 NPU 硬件显存不足以放下完整大模型权重,开启memory_offload,框架自动把权重、激活卸载到 Host 主机内存,计算时再搬回 NPU,属于重要硬件能力配置。

import mindspore as ms

# 开启异构offload,GRAPH模式生效
ms.set_context(
    mode=ms.GRAPH_MODE,
    device_target="Ascend",
    memory_offload="ON",
    max_device_memory="24GB"
)

# offload详细策略配置
offload_cfg = {
    "auto_offload": True,
    "offload_cpu_size":"64GB",
    "enable_pinned_mem": True
}
ms.set_offload_context(offload_config=offload_cfg)

2.6 多卡分布式硬件信息查询

from mindspore.communication import init, get_rank, get_group_size

init()
rank = get_rank()
world_size = get_group_size()
print(f"rank:{rank}, total_card:{world_size}")
ms.set_device("Ascend", rank)

三、硬件操作性能数据采集与瓶颈定位

手动 Host‑Device 拷贝、offload 会引入大量内存搬运开销;使用 msprof 采集性能,重点查看 Memcpy 内存拷贝算子耗时,定位硬件操作带来的性能劣化。

# prof_hw.sh采集硬件操作性能
msprof --application="python3 hardware_op_demo.py" --output="./prof_ms_hw"

# 解析报告
msprof --export=on --output="./prof_ms_hw"

# 过滤查看内存拷贝算子
msprof --analyze=op --output="./prof_ms_hw" | grep -E "op_name|duration|Memcpy"


# Insight可视化查看内存拷贝、算子执行时序
insight ./prof_ms_hw

性能判断:大量 MemcpyAsync 耗时占比高,说明 Host‑Device 拷贝成为性能瓶颈,需要减少频繁手动拷贝,调优 offload 参数。

四、高频硬件操作问题排查与解决方案

问题 1:读取设备张量数值异常,结果全 0 / 随机脏数据

● 现象:执行完算子立刻调用.asnumpy(),拿到结果不正确

● 根因:昇腾算子异步下发,还没有执行完成就拷贝回主机,缺少流同步。

● 解决:读取设备数据前,增加流同步。

import mindspore.runtime as mrt
mrt.synchronize_stream()
result_np = out_tensor.asnumpy()

问题 2:del 删除张量后 NPU 显存没有下降

● 现象:python 执行 del tensor,npu‑smi 观察 NPU 内存没有释放

● 根因:MindSpore 内存池机制,内存保留在池子里用于后续复用,不会直接还给硬件。

● 解决:删除对象后调用runtime.empty_cache(device_id=0)清空内存池。

问题 3:开启 memory_offload 后训练吞吐大幅下降

● 现象:开启异构卸载解决 OOM,但迭代速度明显变慢

● 根因:Host‑NPU 频繁搬移权重、激活,PCIe 带宽成为瓶颈;offloadcpusize 设置过小。

● 解决:

1. 调高offload_cpu_size增大主机可用卸载内存;

2. 开启enable_pinned_mem=True锁页内存,提升拷贝带宽;

3. 优先使用模型并行、张量并行,减少 offload 依赖。

问题 4:多卡训练 rank 进程报 device_id 不匹配

● 现象:分布式训练部分进程报设备 id 错误

● 根因:没有根据 rank 动态设置set_device,全部进程写死 device_id=0。

● 解决:每个 rank 进程,使用 rank 作为 device_id 入参。

ms.set_device("Ascend", device_id=rank)

问题 5:GRAPH 模式内部调用 asnumpy () 直接报错

● 现象:construct Cell 内部写tensor.asnumpy()抛出异常

● 根因:GRAPH 模式 construct 内部为计算图构建阶段,不支持主机侧 numpy 操作。

● 解决:张量拷贝、asnumpy 操作放到 Cell 外部,图外执行。

问题 6:npu‑smi 显示内存占用很高,但runtime.get_memory_info()数值很小

● 现象:两者内存统计不一致

● 根因:CANN 驱动、HCCL 通信库、内存池元数据占用硬件内存,mindspore runtime 统计仅统计 MindSpore 管理张量。

● 解决:npu‑smi 为硬件真实占用,get_memory_info为 MindSpore 张量占用,两者统计口径不同,排查 OOM 优先看 npu‑smi。

五、硬件操作最佳实践

1. 避免频繁手动 Host‑Device 拷贝:大模型训练尽量把计算全部放在 NPU,减少循环内部.asnumpy()、.copy_from_host(),拷贝会严重降低吞吐。

2. 流同步不要滥用:PYNATIVE 模式框架自动同步;GRAPH 模式仅在需要读取硬件结果的时候调用synchronize_stream(),频繁同步会阻塞流水线,性能下降。

3. empty_cache 仅调试使用:正式训练业务不要循环调用 empty_cache,会破坏内存池复用,造成性能损失;只调试阶段手动释放显存。

4. offload 作为兜底手段:优先使用模型并行、重计算降低显存压力,memory_offload作为硬件资源不足兜底方案,会带来拷贝开销。

5. 分布式 rank 绑定设备:多卡场景每个 rank 进程,ms.set_device("Ascend", rank)绑定对应卡号,禁止全部进程固定写死 device_id=0。

提示:大模型推理场景,尽量把全部张量驻留在 NPU 设备,减少 Host‑Device 来回拷贝,才能拿到最优吞吐性能。

Logo

鲲鹏昇腾开发者社区是面向全社会开放的“联接全球计算开发者,聚合华为+生态”的社区,内容涵盖鲲鹏、昇腾资源,帮助开发者快速获取所需的知识、经验、软件、工具、算力,支撑开发者易学、好用、成功,成为核心开发者。

更多推荐