讲 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 中断在任务完成时唤醒等待方。
一句话 :hipEvent → hsa_signal → HsaEvent → 内核事件+中断,是一条完整的"同步信号"翻译链。
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、为什么要分这么多层?
读到这你可能会问:一个显存分配搞四层,是不是过度设计?其实每层都有它非留不可的理由:
- HIP 层为了可移植:把 CUDA 生态的代码几乎零成本搬到 AMD,靠的就是 HIP 这层 API 兼容。
- HSA 层为了标准化:agent/queue/signal 是开放的 HSA 标准,不绑死某家硬件,CPU、GPU、乃至别的加速器都能统一建模。
- Thunk 层为了解耦:让上面的运行时不用直接跟内核 ioctl 打交道,内核接口变了,改这一薄层就行。
- 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_allocate → hsaKmtAllocMemory → KFD 建 BO;hipLaunchKernel 是 HIP → 写 AQL 包 → 敲 doorbell → CP 取包执行。同一个东西每层换个名字,是因为每层站的抽象高度不一样------把这套"改名规律"记住,ROCm 源码读起来就从"迷宫"变成了"地图"。
如果这张对照表对你有用,欢迎收藏,下次读 ROCm 源码时对着查。
👉 觉得有用的话,收藏一下,转给同样在啃 ROCm 的同学。
👉 这几条翻译链里,你最想让我把哪一条拆到源码级?评论区告诉我。