news 2026/7/15 1:28:38

6. ROCm HIP 虚拟内存管理(VMM)接口 hipMemCreate/hipMemMap/hipMemSetAccess 到 libhsakmt 的调用路径分析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
6. ROCm HIP 虚拟内存管理(VMM)接口 hipMemCreate/hipMemMap/hipMemSetAccess 到 libhsakmt 的调用路径分析

本篇从 HIP 用户 API 入口出发,逐层追到 libhsakmt/KFD,重点回答一个技术问题——VMM 接口族和hipMalloc*族到底差在哪里、为什么需要它


1. 核心结论

hipMemCreate/hipMemAddressReserve/hipMemMap/hipMemSetAccess是 HIP 的虚拟内存管理(VMM)API,对标 CUDA 的cuMemCreate/cuMemAddressReserve/cuMemMap/cuMemSetAccess

它和hipMalloc本质差异hipMalloc一步完成「分配物理内存 + 建 VA + 建页表 + 全设备可访问」;VMM 把这四件事拆成四个独立、可分别控制的 API

hipMemAddressReserve → 只要一段虚拟地址(VA),不含物理内存 hipMemCreate → 只分配物理内存,返回句柄,不含 VA hipMemMap → 把「物理句柄」绑定到「VA 区间」 hipMemSetAccess → 逐设备授予 R/W 权限(此步才真正建 GPU 页表)

整条链路的分层与ROCm HIP 内存分配接口到 libhsakmt 的调用路径分析一致,只是后端走的是 ROCr 的 VMM 接口而非 memory pool:

hipMemCreate/hipMemAddressReserve/hipMemMap/hipMemSetAccess->HIP Runtime API 层(hip_vm.cpp)->hip::GenericAllocation/amd::Memory(virtualbuffer 或 phys buffer)->roc::Device::virtual{Alloc,Map,Unmap,Free}/SetMemAccess/deviceVmemAlloc->Hsa::vmem_*(ROCclr 对 ROCr 符号的动态包装)->hsa_amd_vmem_address_reserve/vmem_handle_create/vmem_map/vmem_set_access->ROCr core::Runtime::VMemory*->hsaKmtAllocMemoryAlign(OnlyAddress)/hsaKmtMemoryVaMap/DRM ioctl->KFD

2. 为什么需要 VMM:与 hipMalloc* 的差异(核心技术问题)

2.1 hipMalloc 的「一步到位」模型

回顾ROCm HIP 内存分配接口到 libhsakmt 的调用路径分析:hipMalloc → ihipMalloc → SvmBuffer::malloc → roc::Device::deviceLocalAlloc → hsa_amd_memory_pool_allocate。一次调用里,物理内存、VA、GPU 页表、(默认)本设备可访问性全部就绪。用户只拿到一个void*无法单独控制其中任何一步

这带来几个hipMalloc做不到的事:

需求hipMalloc 为何做不到
预留一大段连续 VA,之后再逐步填物理内存分配即绑定,VA 不可预留
不同来源的物理块(不同 GPU / 跨进程 dmabuf)拼进同一段连续 VA一个指针对应一块 pool 分配
先分配物理内存,稍后再决定映射到哪个 VA、给哪些设备访问物理与 VA 绑死
精确控制「哪个设备以什么权限访问」默认全设备可访问,粒度粗
动态扩容一块 buffer 而保持指针不变(在预留 VA 内追加映射)无法原地扩展

2.2 VMM 的「四步解耦」模型

步骤hipMallocVMM
① 物理内存hipMalloc内隐式完成hipMemCreate→ 返回hipMemGenericAllocationHandle_t
② 虚拟地址隐式,由 runtime 定hipMemAddressReserve→ 返回void*
③ 绑定/页表隐式,全设备hipMemMap(簿记)+hipMemSetAccess(真正建页表)
④ 访问授权隐式,全设备可访问hipMemSetAccess{device, flags}授权

2.3 对象模型差异

ROCm HIP 内存分配接口到 libhsakmt 的调用路径分析 强调两个桥接:hip::Device→amd::Contextvoid*→amd::Memory。VMM 在void*→amd::Memory这一侧多出一层拆分

hipMalloc: void* ptr ──► 一个 amd::Buffer(物理+VA 合一) VMM: hipMemCreate ──► phys amd::Memory(ROCCLR_MEM_PHYMEM)+ hip::GenericAllocation ← handle hipMemAddressReserve ──► parent virtual amd::Memory(只有 VA,无物理) ← ptr hipMemMap ──► 在 parent VA 上切出 sub-buffer,交叉链接到 phys mem
  • 物理侧hipMemCreate造的是ROCCLR_MEM_PHYMEMbuffer,其getUserData().hsa_handle保存 ROCr 返回的hsa_amd_vmem_alloc_handle_t;再包一层hip::GenericAllocation作为用户可见 handle。
  • VA 侧hipMemAddressReserve造的是一个kParent=true的 virtual buffer,只登记 VA。
  • 映射hipMemMap在 parent VA 里切出 sub-buffer,与 phys mem 交叉链接(phys_mem_obj)。这就是为什么hipMemSetAccess/hipMemUnmap里要沿 sub-buffer 链遍历。

3. HIP 层入口模式

所有 VMM API 同样是「HIP_INIT_API+ 参数检查 + 直接实现」,但注意:这些 API 大多是同步直达后端,不走 stream/command 队列(见hip_vm.cpp注释「Direct synchronous path」)。


4. 逐接口调用路径

4.1hipMemAddressReserve—— 只要 VA

hipMemAddressReserve(ptr,size,alignment,addr,flags)// 校验 size 是 virtualMemAllocGranularityMinimum_ 的整数倍、alignment 是 2 的幂->g_devices[0]->devices()[0]->virtualAlloc(addr,size,alignment)// 单次调用即为所有设备预留
roc::Device::virtualAlloc -> Hsa::vmem_address_reserve(&vptr, size, req_addr, 0) // = hsa_amd_vmem_address_reserve -> CreateVirtualBuffer(context, vptr, size, kParent=true) // 建 parent virtual amd::Memory -> 返回 mem->getSvmPtr()

底层:VMemoryAddressReserve → hsaKmtAllocMemoryAlign(OnlyAddress|FixedAddress),只占 VA 不分配物理。

注意:VA 预留对所有设备一次完成(用g_devices[0]即可),因为 VA 空间是进程级的。

4.2hipMemCreate—— 只分配物理内存

hipMemCreate(handle,size,prop,flags)// prop->type ∈ {Pinned, Uncached};location ∈ {Device, Host}ihipFlags=ROCCLR_MEM_PHYMEM[|CL_MEM_SVM_ATOMICS|ROCCLR_MEM_HSA_UNCACHED]amdContext=(location==Host)?host_context:curDevContext->amd::SvmBuffer::malloc(*amdContext,ihipFlags,size,align)// 带 PHYMEM 标志->phys_mem_obj=getMemoryObject(dev,ptr)->phys_mem_obj->getUserData().data=newhip::GenericAllocation(...)->*handle=(hipMemGenericAllocationHandle_t)that GenericAllocation

ROCCLR_MEM_PHYMEM标志让 ROCm 后端走物理内存句柄分配而非普通 pool alloc:

roc::Memory::create(PHYMEM 路径) -> roc::Device::deviceVmemAlloc(size, flags) -> 选 pool(uncached ? gpu_ext_fine_grained : gpuvm_segment_) -> Hsa::vmem_handle_create(pool, size, MEMORY_TYPE_PINNED, 0, &hsa_vmem_handle) // = hsa_amd_vmem_handle_create -> 返回 hsa_vmem_handle.handle → 存入 userData().hsa_handle

底层:VMemoryHandleCreate → region->Allocate + KfdDriver::CreateShareableHandle此时没有 VA、没有页表

对比hipMalloc走的是deviceLocalAlloc → hsa_amd_memory_pool_allocate;VMM 走的是deviceVmemAlloc → hsa_amd_vmem_handle_create。这是两族接口在后端的第一处分岔

4.3hipMemMap—— 绑定物理句柄到 VA(仅簿记)

hipMemMap(ptr,size,offset=0,handle,flags=0)ga=(hip::GenericAllocation*)handle// 校验 owner_dev_id 合法、size 对齐 granularityga->retain()->dev->virtualMap(ptr,size,&ga->asAmdMemory())// 同步直达,不走 stream
roc::Device::virtualMap -> MapMemObjBookkeeping(phys, va, size) // 在 parent VA 上切 sub-buffer -> Hsa::vmem_map(sub_va, size, offset, hsa_handle, 0) // = hsa_amd_vmem_map -> FinalizeMapMemObjBookkeeping(...) // 交叉链接 sub_obj ↔ phys

底层:VMemoryHandleMap只做簿记,无 ioctl——真正建页表在下一步。

4.4hipMemSetAccess—— 逐设备授权(真正建页表)

hipMemSetAccess(ptr,size,desc[],count)ValidateSubBufferCoverage(ptr,size)// size 必须正好覆盖整数个 sub-bufferforeach desc[i]{location,flags}:dev=g_devices[desc[i].location.id]->dev->devices()[0]->SetMemAccess(ptr,size,access_flags,locationType)
roc::Device::SetMemAccess desc.permissions = access_flags // None/ReadOnly/ReadWrite desc.agent_handle = (kDevice) ? getBackendDevice() : getCpuAgent() -> Hsa::vmem_set_access(va, size, &desc, 1) // = hsa_amd_vmem_set_access

底层:VMemorySetAccess → VMemorySetAccessPerHandle,GPU 走KfdDriver::Map → hsaKmtMemoryVaMap建 GPU 页表,CPU 走os::MapMemory这一步之后 GPU 才能真正访问该 VA。

4.5hipMemUnmap/hipMemAddressFree/hipMemRelease—— 逆向拆解

hipMemUnmap(ptr,size) Pass1: 沿 sub-buffer 链校验 phys_mem_obj/ga、收集需同步的设备 Pass2: 对每个相关设备 SyncAllStreams() // 避免 unmap 与在飞行的访问竞争 Pass3: 逐 sub-buffer: sub_dev->virtualUnmap(sub_va,sub_size) -> Hsa::vmem_unmap (= hsa_amd_vmem_unmap) -> ga->release() hipMemAddressFree(ptr,size) -> g_devices[0]->devices()[0]->virtualFree(ptr) -> DestroyVirtualBuffer + Hsa::vmem_address_free (= hsa_amd_vmem_address_free) hipMemRelease(handle) -> ga->release() // 引用计数,GenericAllocation 归零时释放 phys(deviceVmemRelease → vmem_handle_release)

4.6 共享与查询接口

HIP APIroc backendROCr
hipMemExportToShareableHandleExportShareableVMMHandlevmem_export_shareable_handle(dmabuf)/vmem_export_fabric_handle
hipMemImportFromShareableHandleImportShareableVMMHandle → ImportShareableHSAHandlevmem_import_shareable_handle/vmem_import_fabric_handle
hipMemRetainAllocationHandlephys_mem_obj->userData().data(纯 runtime 侧引用计数)
hipMemGetAllocationGranularitydev_info.virtualMemAllocGranularity*
hipMemGetAllocationPropertiesFromHandleGenericAllocation::GetProperties()
hipMemGetAccessGetMemAccessvmem_get_access

5. 完整调用链总览

libhsakmt / KFD / DRM

ROCr hsa_amd_vmem_*

ROCclr roc::Device

HIP 层 (hip_vm.cpp)

hipMemAddressReserve

hipMemCreate

hipMemMap

hipMemSetAccess

virtualAlloc

deviceVmemAlloc

virtualMap

SetMemAccess

vmem_address_reserve

vmem_handle_create

vmem_map

vmem_set_access

hsaKmtAllocMemoryAlign(OnlyAddress)

region Allocate + CreateShareableHandle

(仅簿记, 无 ioctl)

hsaKmtMemoryVaMap (建 GPU 页表)


6. 典型使用序列(对照 hipMalloc)

# hipMalloc 一步到位 ptr = hipMalloc(size); // 物理+VA+页表+全设备可访问 ... use ptr ... hipFree(ptr); # VMM 四步解耦 hipMemAddressReserve(&ptr, size, 0, 0, 0); // ① 预留 VA hipMemCreate(&h, size, &prop, 0); // ② 分配物理 hipMemMap(ptr, size, 0, h, 0); // ③ 绑定 hipMemSetAccess(ptr, size, &desc, 1); // ④ 授权(建页表) ... use ptr ... hipMemUnmap(ptr, size); hipMemRelease(h); hipMemAddressFree(ptr, size);

7. 调试建议(分层断点)

HIP 层:

hipMemAddressReserve / hipMemCreate / hipMemMap / hipMemSetAccess / hipMemUnmap

ROCclr 层(rocdevice.cpp):

roc::Device::virtualAlloc / virtualFree roc::Device::deviceVmemAlloc / deviceVmemRelease roc::Device::virtualMap / virtualUnmap roc::Device::SetMemAccess / GetMemAccess roc::Device::ExportShareableVMMHandle / ImportShareableHSAHandle

ROCr 层:

hsa_amd_vmem_address_reserve / vmem_handle_create / vmem_map / vmem_set_access core::Runtime::VMemoryAddressReserve / VMemoryHandleCreate / VMemoryHandleMap / VMemorySetAccess

libhsakmt 层:

hsaKmtAllocMemoryAlign / hsaKmtMemoryVaMap / hsaKmtMemoryVaUnmap / hsaKmtFreeMemory

推荐第一轮断点(看「预留→创建→映射→授权」四步分岔):

hipMemAddressReserve->virtualAlloc->hsa_amd_vmem_address_reserve hipMemCreate->deviceVmemAlloc->hsa_amd_vmem_handle_create hipMemMap->virtualMap->hsa_amd_vmem_map hipMemSetAccess->SetMemAccess->hsa_amd_vmem_set_access->hsaKmtMemoryVaMap

8. 总结

  • hipMalloc*的根本差异hipMalloc把「物理 + VA + 页表 + 访问」四件事一次做完、粒度粗、指针不可控;VMM 用hipMemCreate/hipMemAddressReserve/hipMemMap/hipMemSetAccess把它们拆成四个可独立控制的步骤,从而支持「预留大段连续 VA、按需拼贴不同物理块、逐设备精确授权、跨进程共享」等hipMalloc无法实现的场景。
  • 对象模型上:VMM 在void*→amd::Memory桥接里把「物理」(ROCCLR_MEM_PHYMEMbuffer +GenericAllocationhandle)和「VA」(parent virtual buffer + sub-buffer)拆开,hipMemMap负责把两者交叉链接。
  • 后端分岔点hipMallocdeviceLocalAlloc → hsa_amd_memory_pool_allocate;VMM 走deviceVmemAlloc → hsa_amd_vmem_handle_create以及virtual* → hsa_amd_vmem_{address_reserve,map,set_access}。再往下(ROCr → libhsakmt → KFD)的细节见下一篇[分析中…]。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/15 1:28:29

GROMACS小分子拓扑文件生成:从基础流程到高级工具全解析

1. GROMACS拓扑文件基础概念刚接触GROMACS模拟时,我最头疼的就是拓扑文件。记得第一次跑蛋白-配体复合物模拟,系统总是莫名其妙崩溃,后来才发现是拓扑文件里氢原子电荷没处理好。拓扑文件本质上就是分子的"身份证",它告…

作者头像 李华
网站建设 2026/7/15 1:25:36

颠覆式Markdown编辑体验:Obsidian Editing Toolbar让创作效率倍增

颠覆式Markdown编辑体验:Obsidian Editing Toolbar让创作效率倍增 【免费下载链接】obsidian-editing-toolbar An obsidian toolbar plugin, modified from the Cmenu plugin 项目地址: https://gitcode.com/gh_mirrors/ob/obsidian-editing-toolbar 在数字笔…

作者头像 李华
网站建设 2026/7/15 1:25:35

USB Type-C PD系统电路保护设计:从TVS、肖特基到RC缓冲的实战解析

1. 项目概述:为什么USB Type-C PD系统需要“贴身保镖”?如果你最近拆解过任何一款支持USB Type-C充电的笔记本、手机或者扩展坞,大概率会在Type-C接口附近看到一堆密密麻麻的小元件:几个不起眼的电容、一两个二极管,或…

作者头像 李华
网站建设 2026/7/15 1:24:39

Linux/macOS原生运行Minecraft基岩版:mcpelauncher跨平台启动器实战指南

1. 项目概述:为什么我们需要一个跨平台启动器?如果你是一名资深的Minecraft玩家,同时又恰好是Linux或macOS用户,那么“启动器”这个词对你来说,可能意味着一段充满妥协和折腾的回忆。长久以来,Minecraft基岩…

作者头像 李华
网站建设 2026/7/15 1:23:54

STM32边缘AI在线量化部署:从模型到MCU的高效推理实践

最近在调试一个基于STM32的智能传感器项目时,遇到了一个典型问题:训练好的AI模型在PC上运行流畅,但移植到STM32后要么内存爆满,要么推理速度慢得无法接受。这让我重新审视了边缘AI部署的核心挑战——如何在资源受限的微控制器上实…

作者头像 李华