
前言昇腾NPU插在主板上PyTorch能直接调用它跑模型这个过程是怎么实现的驱动扮演了什么角色为什么驱动版本不对NPU就跑不起来第一次研究昇腾驱动的时候也被它的分层架构搞得很懵。从PyTorch调用到NPU执行中间经过了哪些层每层负责什么为什么有时候驱动装对了但NPU还是不可用带着这个疑问深入研究了driver仓库的源码发现driver不是简单的硬件驱动而是一个分层软件栈包括HDFHardware Driver Foundation、HDKHardware Development Kit、Runtime Library三层每层各司其职共同完成让PyTorch能用NPU这个目标。本文是概念拆解——会拆开driver的分层架构、核心模块、常见问题解释为什么driver是昇腾CANN的基础层以及为什么它出问题会影响整个栈。driver在CANN五层架构里的位置先说清楚driver住在哪。昇腾CANN的架构分五层driver住在第5层——昇腾计算基础层是整个栈的最底层。第1层昇腾计算语言层 AscendCL └─ 算子开发接口 Ascend C 第2层昇腾计算服务层 ├─ AOL 算子库 ├─ AOE 调优引擎 └─ Framework Adaptor 框架适配器 第3层昇腾计算编译层 ├─ Graph Compiler 图编译器 └─ BiSheng / ATC 编译器 第4层昇腾计算执行层 ├─ Runtime 运行时 ├─ Graph Executor 图执行器 ├─ HCCL 集合通信库 └─ AIPP / DVPP 第5层昇腾计算基础层 ← driver 住在这 ├─ RMSResource Management Service ├─ CMSConfiguration Management Service ├─ DMSDevice Management Service ├─ DRVDRiVer驱动核心 └─ UTILITY 硬件层昇腾 AI 硬件达芬奇架构为啥住第5层因为driver是硬件抽象层是连接软件栈和物理硬件的桥梁。没有driver软件栈就不知道硬件长什么样更不知道怎么控制硬件。依赖关系driver → hardwareNPU。driver直接控制NPU硬件是整个栈的最底层。上层的HCCS、Runtime、AscendCL都依赖driver提供的接口。分层架构driver的三层结构driver不是一层代码而是三层代码HDF层、HDK层、Runtime Library层。第1层HDFHardware Driver FoundationHDF是驱动框架层定义了驱动的标准框架和接口规范。所有的昇腾驱动都要遵循HDF规范这样才能被上层软件统一管理。核心模块// HDF核心设备管理classHdfDeviceManager{public:// 加载设备驱动staticintLoadDevice(constchar*device_name);// 卸载设备驱动staticintUnloadDevice(constchar*device_name);// 获取设备句柄staticvoid*GetDevice(constchar*device_name);};// HDF核心驱动服务classHdfDriverService{public:// 初始化驱动服务virtualintInitialize()0;// 销毁驱动服务virtualintRelease()0;// 绑定设备virtualintBindDevice(structDevice*device)0;// 解绑设备virtualintUnbindDevice(structDevice*device)0;};// NPU驱动服务classNpuDriverService:publicHdfDriverService{public:intInitialize()override;intRelease()override;intBindDevice(structDevice*device)override;intUnbindDevice(structDevice*device)override;// NPU特有接口intSubmitTask(structTask*task);intQueryTask(uint64_ttask_id);intSyncDevice();};关键点HdfDeviceManager设备管理器负责加载/卸载设备驱动HdfDriverService驱动服务基类定义了驱动的生命周期接口NpuDriverServiceNPU驱动服务实现了NPU特有的接口⚠️ 踩坑预警HDF层是内核态代码普通开发者不要直接修改看看就好。第2层HDKHardware Development KitHDK是硬件开发工具包提供了驱动开发的常用工具和接口。开发者基于HDK可以快速开发新的驱动或者扩展现有驱动。核心模块// HDK核心内存管理classHdkMemory{public:// 分配设备内存staticvoid*AllocDeviceMem(size_t size);// 释放设备内存staticvoidFreeDeviceMem(void*ptr);// 分配Host内存锁页内存用于DMAstaticvoid*AllocHostMem(size_t size);// 释放Host内存staticvoidFreeHostMem(void*ptr);// 内存拷贝Device ↔ HoststaticvoidMemcpy(void*dst,constvoid*src,size_t size);};// HDK核心任务调度classHdkTask{public:// 创建任务staticuint64_tCreateTask(structTaskConfig*config);// 提交任务staticintSubmitTask(uint64_ttask_id);// 等待任务完成staticintWaitTask(uint64_ttask_id,uint32_ttimeout_ms);// 取消任务staticintCancelTask(uint64_ttask_id);};// HDK核心事件管理classHdkEvent{public:// 创建事件staticuint64_tCreateEvent();// 等待事件staticintWaitEvent(uint64_tevent_id,uint32_ttimeout_ms);// 触发事件staticintSignalEvent(uint64_tevent_id);// 销毁事件staticintDestroyEvent(uint64_tevent_id);};关键点HdkMemory内存管理封装了设备内存、Host内存、DMA拷贝的接口HdkTask任务调度封装了任务创建、提交、等待、取消的接口HdkEvent事件管理封装了事件创建、等待、触发、销毁的接口⚠️ 踩坑预警HDK层是内核态代码普通开发者不要直接修改看看就好。第3层Runtime Library用户态驱动接口Runtime Library是用户态驱动接口是普通开发者接触driver的唯一途径。PyTorch、AscendCL、Runtime都通过Runtime Library和driver交互。核心模块// Runtime Library核心设备管理classDeviceManager{public:// 初始化设备管理staticintInit();// 销毁设备管理staticintDestroy();// 获取设备数量staticintGetDeviceCount();// 设置当前设备staticintSetDevice(intdevice_id);// 获取当前设备staticintGetDevice();// 同步设备staticintSyncDevice();};// Runtime Library核心内存管理classMemoryManager{public:// 分配设备内存staticvoid*Alloc(size_t size);// 释放设备内存staticvoidFree(void*ptr);// 分配锁页内存用于DMAstaticvoid*AllocPinned(size_t size);// 释放锁页内存staticvoidFreePinned(void*ptr);// 内存拷贝同步staticvoidMemcpy(void*dst,constvoid*src,size_t size);// 内存拷贝异步staticvoidMemcpyAsync(void*dst,constvoid*src,size_t size,Stream stream);};// Runtime Library核心流管理classStreamManager{public:// 创建流staticStreamCreateStream();// 销毁流staticintDestroyStream(Stream stream);// 同步流staticintSyncStream(Stream stream);// 等待流staticintStreamWait(Stream stream,Event event);};关键点DeviceManager设备管理初始化driver、设置当前设备、同步设备MemoryManager内存管理分配/释放设备内存、锁页内存、DMA拷贝StreamManager流管理创建/销毁/同步流⚠️ 踩坑预警Runtime Library是用户态代码可以直接调用但要确保driver版本匹配。核心能力driver到底能干啥driver的核心能力分4类设备管理、内存管理、任务调度、中断处理。1. 设备管理设备管理是让软件知道NPU存在。driver负责枚举NPU设备、加载驱动、初始化设备。代码讲解importacl# 初始化ACLAscend Computing Languageacl.init()# 获取设备数量device_countacl.get_device_count()print(fNPU设备数量:{device_count})# 分配设备device_id0acl.rt.set_device(device_id)# 释放设备acl.rt.reset_device(device_id)# 销毁ACLacl.finalize()代码讲解acl.init()初始化ACL加载driveracl.get_device_count()获取NPU设备数量acl.rt.set_device()分配设备acl.rt.reset_device()释放设备⚠️ 踩坑预警分配设备后一定要释放不然会资源泄漏。2. 内存管理内存管理是让软件能往NPU上写数据。driver负责分配设备内存、Host内存、DMA拷贝。代码讲解importnumpyasnpimportacl# 分配设备内存size1024*4# 1024个float32ptracl.rt.malloc(size,acl.RT_MEM_MALLOC_NORMAL_ONLY)print(f设备内存地址:{hex(ptr)})# 从numpy数组拷贝数据到设备host_datanp.random.randn(1024).astype(np.float32)acl.rt.memcpy(ptr,size,host_data.ctypes.data,size,acl.RT_MEMCPY_HOST_TO_DEVICE)# 从设备拷贝数据回numpy数组resultnp.empty(1024,dtypenp.float32)acl.rt.memcpy(result.ctypes.data,size,ptr,size,acl.RT_MEMCPY_DEVICE_TO_HOST)print(f结果:{result[:5]})# 释放设备内存acl.rt.free(ptr)代码讲解acl.rt.malloc()分配设备内存acl.rt.memcpy()DMA拷贝H2D或D2Hacl.rt.free()释放设备内存⚠️ 踩坑预警设备内存要手动释放不然会内存泄漏。3. 任务调度任务调度是让软件能往NPU上提交计算任务。driver负责创建任务、提交任务、等待任务完成。代码讲解importacl# 创建流stream,retacl.rt.create_stream()print(f流创建成功:{stream})# 创建任务以VectorAdd为例task_config{op_type:VectorAdd,input_x:ptr_x,input_y:ptr_y,output_z:ptr_z,count:1024}task_idacl.rt.launch_task(stream,task_config)print(f任务提交成功:{task_id})# 等待任务完成acl.rt.stream_synchronize(stream)print(f任务执行完成)# 销毁流acl.rt.destroy_stream(stream)代码讲解acl.rt.create_stream()创建流acl.rt.launch_task()提交任务acl.rt.stream_synchronize()等待流完成⚠️ 踩坑预警提交任务后一定要等待完成不然结果还没算出来就读了。4. 中断处理中断处理是让NPU能主动通知CPU。driver负责注册中断处理函数、处理中断、唤醒等待的CPU核。代码讲解importacl# 注册中断回调函数definterrupt_callback(interrupt_type,task_id,user_data):print(f中断类型:{interrupt_type}, 任务ID:{task_id})# 处理中断...# 注册中断回调acl.rt.register_interrupt_callback(interrupt_callback,None)# 提交一个需要中断通知的任务task_idacl.rt.launch_task_with_interrupt(stream,task_config)# CPU继续做其他事情等中断唤醒print(CPU继续做其他事情...)# 等NPU发来中断...代码讲解acl.rt.register_interrupt_callback()注册中断回调函数acl.rt.launch_task_with_interrupt()提交需要中断通知的任务⚠️ 踩坑预警中断回调函数要尽快返回不然会影响其他中断。常见问题driver出错了怎么办问题1驱动版本不匹配现象运行acl.init()报错说driver version mismatch。原因CANN版本和driver版本不匹配。解决去昇腾社区下载和CANN版本匹配的driver。# 查看当前driver版本cat/usr/local/Ascend/driver/version.info# 查看CANN版本atc--version# 如果版本不匹配重新安装匹配的driverbashascend-installer-8.0.run--full--driver问题2设备不可用现象运行acl.rt.set_device(0)报错说device not found。原因driver没加载或者NPU硬件故障。解决检查driver加载状态和NPU硬件状态。# 检查driver是否加载lsmod|grepascend# 如果没加载加载drivermodprobe ascend_driver# 检查NPU硬件状态npu-smi info# 如果NPU硬件故障重启机器或者联系售后问题3内存分配失败现象运行acl.rt.malloc()报错说out of memory。原因设备内存不足或者内存碎片太多。解决释放不必要的内存或者重启进程。# 检查设备内存使用情况mem_infoacl.rt.get_mem_info(acl.RT_MEM_INFO_TOTAL)print(f设备总内存:{mem_info[total]/1024**3:.2f}GB)mem_infoacl.rt.get_mem_info(acl.RT_MEM_INFO_USED)print(f设备已用内存:{mem_info[used]/1024**3:.2f}GB)# 释放不必要的内存dellarge_tensor# Python层面释放acl.rt.reset_device(0)# 重置设备释放所有设备内存结尾driver是昇腾CANN的基础层住在第5层昇腾计算基础层用HDFHDKRuntime Library三层架构实现了让PyTorch能用NPU这个目标。如果遇到driver问题版本不匹配、设备不可用、内存分配失败等先检查driver加载状态和版本信息确认没问题再往上层排查。driver是整个栈的最底层driver出问题上层全都受影响。昇腾CANN的驱动潜力还很大driver仓库还有很多细节值得研究。如果想深入了解driver的实现细节欢迎去AtomGit上的昇腾CANN开源社区逛逛里面有一手资料和活跃社区。https://atomgit.com/cann/driver