读懂 ROCm 分层:HIP → ROCr → libhsakmt 核心概念对照表

讲 ROCm 各层功能和调用关系的文章不少,但真正抓住某一个概念、从上到下一条龙拆到底的却很少。因为工作恰好完整趟过整个栈,这里我把自己的梳理总结成一张对照表,分享给同样想搞懂 ROCm 分层的技术读友。


先抛个问题勾一下。

你在 PyTorch 或自己的 HIP 代码里写下这么两行:

cpp 复制代码
hipMalloc(&ptr, n);              // 申请一块显存
hipLaunchKernelGGL(kernel, ...); // 启动一个核函数

它们看着简单,但每一行往下都要穿过 HIP → ROCr → libhsakmt → KFD 四层,才最终落到 GPU 硬件上。麻烦的是:同一个概念在每一层都换了个名字------你以为的"流(stream)",到了下一层叫"队列(queue)",再往下又变成内核里的一个 MQD(Memory Queue Descriptor)。名字对不上,读源码时就容易断片。

这里我试图把这套分层和"改名规律"彻底讲清楚,给你一张能贴在显示器边上的对照表。

1、先立分层模型:四层各管什么

ROCm 计算栈从上到下是四层,职责非常清晰:
#mermaid-svg-OAXApO5AcGeGqiwU{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-OAXApO5AcGeGqiwU .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-OAXApO5AcGeGqiwU .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-OAXApO5AcGeGqiwU .error-icon{fill:#552222;}#mermaid-svg-OAXApO5AcGeGqiwU .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-OAXApO5AcGeGqiwU .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-OAXApO5AcGeGqiwU .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-OAXApO5AcGeGqiwU .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-OAXApO5AcGeGqiwU .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-OAXApO5AcGeGqiwU .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-OAXApO5AcGeGqiwU .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-OAXApO5AcGeGqiwU .marker{fill:#333333;stroke:#333333;}#mermaid-svg-OAXApO5AcGeGqiwU .marker.cross{stroke:#333333;}#mermaid-svg-OAXApO5AcGeGqiwU svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-OAXApO5AcGeGqiwU p{margin:0;}#mermaid-svg-OAXApO5AcGeGqiwU .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-OAXApO5AcGeGqiwU .cluster-label text{fill:#333;}#mermaid-svg-OAXApO5AcGeGqiwU .cluster-label span{color:#333;}#mermaid-svg-OAXApO5AcGeGqiwU .cluster-label span p{background-color:transparent;}#mermaid-svg-OAXApO5AcGeGqiwU .label text,#mermaid-svg-OAXApO5AcGeGqiwU span{fill:#333;color:#333;}#mermaid-svg-OAXApO5AcGeGqiwU .node rect,#mermaid-svg-OAXApO5AcGeGqiwU .node circle,#mermaid-svg-OAXApO5AcGeGqiwU .node ellipse,#mermaid-svg-OAXApO5AcGeGqiwU .node polygon,#mermaid-svg-OAXApO5AcGeGqiwU .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-OAXApO5AcGeGqiwU .rough-node .label text,#mermaid-svg-OAXApO5AcGeGqiwU .node .label text,#mermaid-svg-OAXApO5AcGeGqiwU .image-shape .label,#mermaid-svg-OAXApO5AcGeGqiwU .icon-shape .label{text-anchor:middle;}#mermaid-svg-OAXApO5AcGeGqiwU .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-OAXApO5AcGeGqiwU .rough-node .label,#mermaid-svg-OAXApO5AcGeGqiwU .node .label,#mermaid-svg-OAXApO5AcGeGqiwU .image-shape .label,#mermaid-svg-OAXApO5AcGeGqiwU .icon-shape .label{text-align:center;}#mermaid-svg-OAXApO5AcGeGqiwU .node.clickable{cursor:pointer;}#mermaid-svg-OAXApO5AcGeGqiwU .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-OAXApO5AcGeGqiwU .arrowheadPath{fill:#333333;}#mermaid-svg-OAXApO5AcGeGqiwU .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-OAXApO5AcGeGqiwU .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-OAXApO5AcGeGqiwU .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-OAXApO5AcGeGqiwU .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-OAXApO5AcGeGqiwU .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-OAXApO5AcGeGqiwU .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-OAXApO5AcGeGqiwU .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-OAXApO5AcGeGqiwU .cluster text{fill:#333;}#mermaid-svg-OAXApO5AcGeGqiwU .cluster span{color:#333;}#mermaid-svg-OAXApO5AcGeGqiwU div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-OAXApO5AcGeGqiwU .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-OAXApO5AcGeGqiwU rect.text{fill:none;stroke-width:0;}#mermaid-svg-OAXApO5AcGeGqiwU .icon-shape,#mermaid-svg-OAXApO5AcGeGqiwU .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-OAXApO5AcGeGqiwU .icon-shape p,#mermaid-svg-OAXApO5AcGeGqiwU .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-OAXApO5AcGeGqiwU .icon-shape .label rect,#mermaid-svg-OAXApO5AcGeGqiwU .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-OAXApO5AcGeGqiwU .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-OAXApO5AcGeGqiwU .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-OAXApO5AcGeGqiwU :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 应用 / PyTorch / TensorFlow 等
HIP(libamdhip64)

CUDA 式 API,你写的代码
ROCr / HSA 运行时(libhsa-runtime64)

HSA 标准:agent / queue / signal / AQL
libhsakmt(ROCt / Thunk)

对 KFD ioctl 的薄封装
KFD(内核态计算驱动)

真正操作硬件
GPU 硬件

一句话概括每层的活:

  • HIP :给你一套顺手的、CUDA 风格的 API(hip*)。它的核心价值是可移植------同一份代码能在 AMD 和 N 卡上跑。但我觉得正是为了学CUDA,导致hip层概念向下对应时乱乱的,不知道兼容CUDA的国产GPU的软件栈是不是做的更好些,欢迎讨论。
  • ROCr / HSA 运行时 :把 HIP 的请求翻译成 HSA 标准里的对象------agent、queue、signal、AQL 包。这层是"标准层",定义了异构计算该长什么样。
  • libhsakmt(Thunk) :一层很薄的库,几乎不做逻辑,只把 HSA 的操作翻译成对内核 KFD 的 ioctl 调用。名字里的 kmt 就是 Kernel Mode Thunk。
  • KFD:内核态计算驱动,真正去创建队列、分配 BO、注册中断、操作 GPUVM。

记住一个心法:越往上越"好用",越往下越"贴硬件";每下一层,都是把上一层的抽象翻译成更接近硬件的语言。

2、镇文之表:核心概念四层对照

这是全文最该收藏的一张表。把最常用的六类概念,在四层里的对应名字全列出来:

概念 HIP 层 ROCr / HSA 层 libhsakmt 层 KFD 内核
设备 hipDevice_t / hipCtx_t hsa_agent_t(GPU agent) HsaNodeProperties / NodeId topology node、gpu_id
流 / 队列 hipStream_t hsa_queue_t(AQL 队列) hsaKmtCreateQueue create_queue ioctl、MQD、doorbell
事件 / 信号 hipEvent_t hsa_signal_t hsaKmtCreateEvent / HsaEvent kfd event、中断
kernel 启动 hipLaunchKernelGGL AQL dispatch packet + 敲 doorbell doorbell 映射 CP / MES 取包执行微码
显存分配 hipMalloc hsa_amd_memory_pool_allocate hsaKmtAllocMemory + MapMemoryToGPU alloc BO ioctl、GPUVM map
统一内存 / SVM hipMallocManaged(分配) hipMemAdvise(设访问偏好) hsa_amd_svm_attributes_set / hsa_amd_svm_prefetch_async hsaKmtSVMSetAttr kfd svm ioctl、HMM 按需迁移

看这张表最该体会的,是改名背后的规律 :不是随便换词,而是每层站的抽象高度不同------HIP 关心"我这个程序里的一条流",HSA 关心"符合标准的一个队列对象",Thunk 关心"该调哪个 ioctl",KFD 关心"内核里那个 MQD 结构和 doorbell 页"。名字变了,是因为关注点变了

下面挑三条最典型的"翻译链"走一遍,你就彻底通了。

3、翻译链一:设备是怎么从 hipDevice 变成 gpu_id 的

#mermaid-svg-I5ovOFvjQiV7JeRr{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-I5ovOFvjQiV7JeRr .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-I5ovOFvjQiV7JeRr .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-I5ovOFvjQiV7JeRr .error-icon{fill:#552222;}#mermaid-svg-I5ovOFvjQiV7JeRr .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-I5ovOFvjQiV7JeRr .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-I5ovOFvjQiV7JeRr .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-I5ovOFvjQiV7JeRr .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-I5ovOFvjQiV7JeRr .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-I5ovOFvjQiV7JeRr .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-I5ovOFvjQiV7JeRr .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-I5ovOFvjQiV7JeRr .marker{fill:#333333;stroke:#333333;}#mermaid-svg-I5ovOFvjQiV7JeRr .marker.cross{stroke:#333333;}#mermaid-svg-I5ovOFvjQiV7JeRr svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-I5ovOFvjQiV7JeRr p{margin:0;}#mermaid-svg-I5ovOFvjQiV7JeRr .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-I5ovOFvjQiV7JeRr .cluster-label text{fill:#333;}#mermaid-svg-I5ovOFvjQiV7JeRr .cluster-label span{color:#333;}#mermaid-svg-I5ovOFvjQiV7JeRr .cluster-label span p{background-color:transparent;}#mermaid-svg-I5ovOFvjQiV7JeRr .label text,#mermaid-svg-I5ovOFvjQiV7JeRr span{fill:#333;color:#333;}#mermaid-svg-I5ovOFvjQiV7JeRr .node rect,#mermaid-svg-I5ovOFvjQiV7JeRr .node circle,#mermaid-svg-I5ovOFvjQiV7JeRr .node ellipse,#mermaid-svg-I5ovOFvjQiV7JeRr .node polygon,#mermaid-svg-I5ovOFvjQiV7JeRr .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-I5ovOFvjQiV7JeRr .rough-node .label text,#mermaid-svg-I5ovOFvjQiV7JeRr .node .label text,#mermaid-svg-I5ovOFvjQiV7JeRr .image-shape .label,#mermaid-svg-I5ovOFvjQiV7JeRr .icon-shape .label{text-anchor:middle;}#mermaid-svg-I5ovOFvjQiV7JeRr .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-I5ovOFvjQiV7JeRr .rough-node .label,#mermaid-svg-I5ovOFvjQiV7JeRr .node .label,#mermaid-svg-I5ovOFvjQiV7JeRr .image-shape .label,#mermaid-svg-I5ovOFvjQiV7JeRr .icon-shape .label{text-align:center;}#mermaid-svg-I5ovOFvjQiV7JeRr .node.clickable{cursor:pointer;}#mermaid-svg-I5ovOFvjQiV7JeRr .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-I5ovOFvjQiV7JeRr .arrowheadPath{fill:#333333;}#mermaid-svg-I5ovOFvjQiV7JeRr .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-I5ovOFvjQiV7JeRr .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-I5ovOFvjQiV7JeRr .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-I5ovOFvjQiV7JeRr .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-I5ovOFvjQiV7JeRr .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-I5ovOFvjQiV7JeRr .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-I5ovOFvjQiV7JeRr .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-I5ovOFvjQiV7JeRr .cluster text{fill:#333;}#mermaid-svg-I5ovOFvjQiV7JeRr .cluster span{color:#333;}#mermaid-svg-I5ovOFvjQiV7JeRr div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-I5ovOFvjQiV7JeRr .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-I5ovOFvjQiV7JeRr rect.text{fill:none;stroke-width:0;}#mermaid-svg-I5ovOFvjQiV7JeRr .icon-shape,#mermaid-svg-I5ovOFvjQiV7JeRr .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-I5ovOFvjQiV7JeRr .icon-shape p,#mermaid-svg-I5ovOFvjQiV7JeRr .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-I5ovOFvjQiV7JeRr .icon-shape .label rect,#mermaid-svg-I5ovOFvjQiV7JeRr .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-I5ovOFvjQiV7JeRr .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-I5ovOFvjQiV7JeRr .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-I5ovOFvjQiV7JeRr :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} hipDevice_t

(HIP:第几号卡)
hsa_agent_t

(HSA:一个计算 agent)
node / NodeId

(Thunk:拓扑节点)
gpu_id

(KFD:拓扑里的唯一 id)

  • 你用 hipSetDevice(0) 选卡,HIP 眼里它就是"第 0 号设备"。
  • 到 ROCr,这块卡是一个 hsa_agent_t------注意 HSA 里 CPU 也是 agent,GPU 只是其中一种,这是标准化带来的统一抽象。
  • 再往下,libhsakmt 通过 hsaKmtAcquireSystemProperties 拿到系统里所有拓扑节点(node),每个 GPU 对应一个 NodeId。
  • 最终在 KFD 里,它是拓扑表中的一项,有个唯一的 gpu_id ,你甚至能在 /sys/class/kfd/kfd/topology/ 下看到。

一句话:同一块卡,从"第几号"一路具体成"内核拓扑里的 gpu_id"。

4、翻译链二:stream 到底是不是 queue

这条最容易让人懵,因为 HIP 用户天天说"流",但底层根本没有"流"这个东西------只有队列
#mermaid-svg-ZVPpdByZ6UKqodVP{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ZVPpdByZ6UKqodVP .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ZVPpdByZ6UKqodVP .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ZVPpdByZ6UKqodVP .error-icon{fill:#552222;}#mermaid-svg-ZVPpdByZ6UKqodVP .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ZVPpdByZ6UKqodVP .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ZVPpdByZ6UKqodVP .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ZVPpdByZ6UKqodVP .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ZVPpdByZ6UKqodVP .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ZVPpdByZ6UKqodVP .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ZVPpdByZ6UKqodVP .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ZVPpdByZ6UKqodVP .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ZVPpdByZ6UKqodVP .marker.cross{stroke:#333333;}#mermaid-svg-ZVPpdByZ6UKqodVP svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ZVPpdByZ6UKqodVP p{margin:0;}#mermaid-svg-ZVPpdByZ6UKqodVP .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ZVPpdByZ6UKqodVP .cluster-label text{fill:#333;}#mermaid-svg-ZVPpdByZ6UKqodVP .cluster-label span{color:#333;}#mermaid-svg-ZVPpdByZ6UKqodVP .cluster-label span p{background-color:transparent;}#mermaid-svg-ZVPpdByZ6UKqodVP .label text,#mermaid-svg-ZVPpdByZ6UKqodVP span{fill:#333;color:#333;}#mermaid-svg-ZVPpdByZ6UKqodVP .node rect,#mermaid-svg-ZVPpdByZ6UKqodVP .node circle,#mermaid-svg-ZVPpdByZ6UKqodVP .node ellipse,#mermaid-svg-ZVPpdByZ6UKqodVP .node polygon,#mermaid-svg-ZVPpdByZ6UKqodVP .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ZVPpdByZ6UKqodVP .rough-node .label text,#mermaid-svg-ZVPpdByZ6UKqodVP .node .label text,#mermaid-svg-ZVPpdByZ6UKqodVP .image-shape .label,#mermaid-svg-ZVPpdByZ6UKqodVP .icon-shape .label{text-anchor:middle;}#mermaid-svg-ZVPpdByZ6UKqodVP .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ZVPpdByZ6UKqodVP .rough-node .label,#mermaid-svg-ZVPpdByZ6UKqodVP .node .label,#mermaid-svg-ZVPpdByZ6UKqodVP .image-shape .label,#mermaid-svg-ZVPpdByZ6UKqodVP .icon-shape .label{text-align:center;}#mermaid-svg-ZVPpdByZ6UKqodVP .node.clickable{cursor:pointer;}#mermaid-svg-ZVPpdByZ6UKqodVP .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ZVPpdByZ6UKqodVP .arrowheadPath{fill:#333333;}#mermaid-svg-ZVPpdByZ6UKqodVP .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ZVPpdByZ6UKqodVP .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ZVPpdByZ6UKqodVP .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ZVPpdByZ6UKqodVP .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ZVPpdByZ6UKqodVP .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ZVPpdByZ6UKqodVP .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ZVPpdByZ6UKqodVP .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ZVPpdByZ6UKqodVP .cluster text{fill:#333;}#mermaid-svg-ZVPpdByZ6UKqodVP .cluster span{color:#333;}#mermaid-svg-ZVPpdByZ6UKqodVP div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ZVPpdByZ6UKqodVP .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ZVPpdByZ6UKqodVP rect.text{fill:none;stroke-width:0;}#mermaid-svg-ZVPpdByZ6UKqodVP .icon-shape,#mermaid-svg-ZVPpdByZ6UKqodVP .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ZVPpdByZ6UKqodVP .icon-shape p,#mermaid-svg-ZVPpdByZ6UKqodVP .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ZVPpdByZ6UKqodVP .icon-shape .label rect,#mermaid-svg-ZVPpdByZ6UKqodVP .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ZVPpdByZ6UKqodVP .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ZVPpdByZ6UKqodVP .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ZVPpdByZ6UKqodVP :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} hipStream_t

(HIP:一条流)
hsa_queue_t

(HSA:AQL 用户态队列)
hsaKmtCreateQueue

(Thunk:建队列 ioctl)
MQD + doorbell

(KFD:队列描述符 + 门铃)

  • HIP 的 hipStream_t 是"一串按顺序执行的任务"这个编程概念。
  • 到 ROCr,它落地成一个 hsa_queue_t ------一块用户态可写的环形缓冲区,你把 AQL 包(HSA 的标准命令格式)写进去。
  • libhsakmt 用 hsaKmtCreateQueue 把这个队列注册到内核。
  • KFD 在内核里为它建一个 MQD(Memory Queue Descriptor,队列描述符) ,并映射一个 doorbell(门铃)------你往队列里写完包,敲一下门铃,硬件的命令处理器(CP/MES)就来取包执行。

一句话:你说的"流",本质是"一个能敲门铃的 AQL 用户态队列"。理解了这条,你就理解了 ROCm"用户态直接提交、绕开内核"的高性能设计精髓。

5、翻译链三:event 与 signal 的对应

#mermaid-svg-t0zcuu9obvDYetKF{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-t0zcuu9obvDYetKF .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-t0zcuu9obvDYetKF .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-t0zcuu9obvDYetKF .error-icon{fill:#552222;}#mermaid-svg-t0zcuu9obvDYetKF .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-t0zcuu9obvDYetKF .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-t0zcuu9obvDYetKF .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-t0zcuu9obvDYetKF .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-t0zcuu9obvDYetKF .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-t0zcuu9obvDYetKF .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-t0zcuu9obvDYetKF .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-t0zcuu9obvDYetKF .marker{fill:#333333;stroke:#333333;}#mermaid-svg-t0zcuu9obvDYetKF .marker.cross{stroke:#333333;}#mermaid-svg-t0zcuu9obvDYetKF svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-t0zcuu9obvDYetKF p{margin:0;}#mermaid-svg-t0zcuu9obvDYetKF .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-t0zcuu9obvDYetKF .cluster-label text{fill:#333;}#mermaid-svg-t0zcuu9obvDYetKF .cluster-label span{color:#333;}#mermaid-svg-t0zcuu9obvDYetKF .cluster-label span p{background-color:transparent;}#mermaid-svg-t0zcuu9obvDYetKF .label text,#mermaid-svg-t0zcuu9obvDYetKF span{fill:#333;color:#333;}#mermaid-svg-t0zcuu9obvDYetKF .node rect,#mermaid-svg-t0zcuu9obvDYetKF .node circle,#mermaid-svg-t0zcuu9obvDYetKF .node ellipse,#mermaid-svg-t0zcuu9obvDYetKF .node polygon,#mermaid-svg-t0zcuu9obvDYetKF .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-t0zcuu9obvDYetKF .rough-node .label text,#mermaid-svg-t0zcuu9obvDYetKF .node .label text,#mermaid-svg-t0zcuu9obvDYetKF .image-shape .label,#mermaid-svg-t0zcuu9obvDYetKF .icon-shape .label{text-anchor:middle;}#mermaid-svg-t0zcuu9obvDYetKF .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-t0zcuu9obvDYetKF .rough-node .label,#mermaid-svg-t0zcuu9obvDYetKF .node .label,#mermaid-svg-t0zcuu9obvDYetKF .image-shape .label,#mermaid-svg-t0zcuu9obvDYetKF .icon-shape .label{text-align:center;}#mermaid-svg-t0zcuu9obvDYetKF .node.clickable{cursor:pointer;}#mermaid-svg-t0zcuu9obvDYetKF .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-t0zcuu9obvDYetKF .arrowheadPath{fill:#333333;}#mermaid-svg-t0zcuu9obvDYetKF .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-t0zcuu9obvDYetKF .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-t0zcuu9obvDYetKF .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-t0zcuu9obvDYetKF .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-t0zcuu9obvDYetKF .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-t0zcuu9obvDYetKF .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-t0zcuu9obvDYetKF .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-t0zcuu9obvDYetKF .cluster text{fill:#333;}#mermaid-svg-t0zcuu9obvDYetKF .cluster span{color:#333;}#mermaid-svg-t0zcuu9obvDYetKF div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-t0zcuu9obvDYetKF .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-t0zcuu9obvDYetKF rect.text{fill:none;stroke-width:0;}#mermaid-svg-t0zcuu9obvDYetKF .icon-shape,#mermaid-svg-t0zcuu9obvDYetKF .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-t0zcuu9obvDYetKF .icon-shape p,#mermaid-svg-t0zcuu9obvDYetKF .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-t0zcuu9obvDYetKF .icon-shape .label rect,#mermaid-svg-t0zcuu9obvDYetKF .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-t0zcuu9obvDYetKF .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-t0zcuu9obvDYetKF .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-t0zcuu9obvDYetKF :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} hipEvent_t

(HIP:事件)
hsa_signal_t

(HSA:信号量)
hsaKmtCreateEvent

(Thunk:HsaEvent)
kfd event + 中断

(KFD:内核事件)

  • HIP 里你用 hipEvent_t 做计时、做流之间的等待。
  • ROCr 把它实现成 hsa_signal_t------一个可以被"等待/唤醒"的信号值,AQL barrier 包也靠它表达依赖。
  • libhsakmt 用 hsaKmtCreateEvent 创建一个 HsaEvent
  • KFD 在内核里管理这个事件,配合 GPU 中断在任务完成时唤醒等待方。

一句话hipEventhsa_signalHsaEvent → 内核事件+中断,是一条完整的"同步信号"翻译链。

6、进阶补充:其余概念对照

上面六类是主干。下面这些偏细,用得着再查------不影响主线阅读。

更多概念的四层对照(pinned / 拷贝 / 内存池 / 地址空间 / 多卡 / 代码对象):

概念 HIP 层 ROCr / HSA 层 libhsakmt 层 KFD 内核
主机 pinned 内存 hipHostMalloc / hipHostRegister hsa_amd_memory_lock hsaKmtRegisterMemory userptr
主机↔设备拷贝 hipMemcpy(Async) hsa_amd_memory_async_copy (提交到 SDMA 队列) SDMA 引擎
内存池 / 区域 (隐式) hsa_amd_memory_pool_t / region HsaMemFlags、heap 类型 VRAM / GTT / USERPTR
地址空间 / 映射 裸指针 flat address hsaKmtMapMemoryToGPU(GPUVA) GPUVM、VA range
多卡 P2P 互访 hipDeviceEnablePeerAccess hsa_amd_agents_allow_access 多 node MapMemoryToGPU map 到多个 gpu
代码对象 / 模块 hipModule_t hsa_executable_t / code object --- ---

几个值得留意的点:

  • 拷贝其实也走队列hipMemcpyAsync 底层是提交到 SDMA(DMA 拷贝引擎) 的队列,和 kernel 走的计算队列是两套,这样拷贝和计算能并行。
  • 统一内存往下就是 SVM :核心表里 hipMallocManaged / hipMemAdvise 那条链,正好接上《AMD vs Intel vs NVIDIA:三家异构内存管理(SVM)》讲的 KFD SVM + HMM,可以对着读。
  • 地址空间hsaKmtMapMemoryToGPU 这一步才是真正把内存映射进 GPU 虚拟地址空间(GPUVA)的关键动作,很多"能分配但 GPU 访问崩"的问题都出在这一步没做对。

7、为什么要分这么多层?

读到这你可能会问:一个显存分配搞四层,是不是过度设计?其实每层都有它非留不可的理由:

  1. HIP 层为了可移植:把 CUDA 生态的代码几乎零成本搬到 AMD,靠的就是 HIP 这层 API 兼容。
  2. HSA 层为了标准化:agent/queue/signal 是开放的 HSA 标准,不绑死某家硬件,CPU、GPU、乃至别的加速器都能统一建模。
  3. Thunk 层为了解耦:让上面的运行时不用直接跟内核 ioctl 打交道,内核接口变了,改这一薄层就行。
  4. KFD 层为了"内核只做该做的事":内核只提供最小必要的机制(建队列、分配内存、转发中断),策略都留在用户态------这正是 ROCm 高性能的根基(用户态直接提交命令,不用每次都陷入内核)。

一句话:这四层不是叠床架屋,而是"可移植 / 标准化 / 解耦 / 最小内核"四个目标各要一层。

8、想往下深挖:每层对应的专栏

这篇是"地图",每一层的"逐行源码级实现"我在这些专栏里拆过:

想深入哪层 推荐专栏
ROCr + libhsakmt 源码 ROCm rocr 与 libhsakmt(thunk) 源码剖析
ROCm 整体机制 深探 ROCm
KFD 内存对象(BO) AMD KFD BO 内存管理剖析
统一内存(SVM) AMD KFD SVM 原理与实现剖析
驱动栈全景 AMDGPU ROCm 驱动栈全解析

9、写在最后

回到开头那两行代码。现在你应该能一眼看穿:hipMalloc 是 HIP → hsa_amd_memory_pool_allocatehsaKmtAllocMemory → KFD 建 BO;hipLaunchKernel 是 HIP → 写 AQL 包 → 敲 doorbell → CP 取包执行。同一个东西每层换个名字,是因为每层站的抽象高度不一样------把这套"改名规律"记住,ROCm 源码读起来就从"迷宫"变成了"地图"。

如果这张对照表对你有用,欢迎收藏,下次读 ROCm 源码时对着查。

👉 觉得有用的话,收藏一下,转给同样在啃 ROCm 的同学。

👉 这几条翻译链里,你最想让我把哪一条拆到源码级?评论区告诉我。

相关推荐
DeeplyMind2 小时前
第14章:rocr-runtime 层的内存分配、查询与释放
rocr-runtime·memoryregion
DeeplyMind10 天前
3. ROCm HIP 内存分配接口到 libhsakmt 的调用路径分析
libhsakmt·hipmalloc·mallocmanaged
光明顶魔镜工程师2 个月前
MIGraphX Lowering 模块深度分析
rocm·migraphx
liuyunshengsir2 个月前
使用HIP编写GPU 算子向量加法
hip
DeeplyMind2 个月前
05 - rocrtst 功能测试详解
rocm·rocr·rocrtst
DeeplyMind2 个月前
ROCm rocr-libhsakmt分析系列5-1: Event 机制全景—从 Doorbell 到 Signal
libhsakmt·rocr-runtime·hsa event
程序设计实验室2 个月前
ROCm on Windows 性能排查:RX 6650 XT 跑 PyTorch,为什么加速不明显?
ai·rocm
DeeplyMind3 个月前
Rocm rocr-libhsakmt Event 机制技术文章预告
libhsakmt·rocr-runtime·hsa event
DeeplyMind4 个月前
AMDGPU驱动中Doorbell与Event机制简要对比
rocm·doorbell·hsa event