CHAPTER 39 / AMD · HIP 与 ROCm

ROCm 各层:一次请求由谁负责

HIP、ROCr、KFD、amdgpu 和硬件分别处理什么?

阅读与推演约 55 分钟练习时间另计

这一章要弄清楚

  • 区分编译路径与执行路径
  • 解释用户态队列及驱动职责
  • 按故障层次提出定位证据

先备知识:进程、系统调用与文件描述符 / 虚拟内存、页表与 mmap / HIP:从 CPU 数据到一次 kernel 调用

C++20 本机逻辑模型;不是设备仿真或性能测量。厂商语法片段未在设备或 SDK 执行,实际 API 以匹配版本的公开文档为准。

同一张图里其实有两条路径

看到HIP、LLVM、ROCr、KFD这些缩写时,先问正在讨论“把程序变成可执行代码”,还是“让现有代码在设备运行”。编译路径处理源代码、类型、优化、目标指令和编译产物;执行路径处理内存、装载、队列、同步和错误。两条路径有关联,却不是每次加法都依次经过所有软件层。

以输出数组加一为例,编译器事先生成目标代码。运行时选择合适产物,准备kernel参数和相关资源,将工作提交到设备。数据准备失败、找不到匹配产物、提交失败、执行访问错误、算法答案错误,分别出现在不同位置。先给失败分类,才能决定看编译日志、API返回值、系统日志还是结果数组。

高层接口到系统资源

HIP提供应用常用的内存、启动和同步接口。在AMD的软件栈中,ROCr是HSA runtime实现,提供agent、queue、signal等更接近派发机制的抽象。把它们区分开,不是要求第一周就绕过HIP写底层队列,而是理解高层API下面仍有资源管理和同步合同。

Linux的amdgpu及相关计算支持负责设备和系统资源管理;KFD涉及计算进程与GPU资源的连接。内存映射、调度与异常恢复等职责不会因为用户态能提交某些队列工作而消失。具体职责还随内核、runtime、平台和操作变化,不应把软件栈画成永远固定的九层流水线。

队列比“调用一次函数”多了什么

例一是一格任务队列的CPU逻辑模型:先写任务内容,再把ready标记置真,消费者才能读任务。它有意不创建线程,因此没有实现真实内存序或硬件doorbell。它只提醒你,发布通知和准备内容是两件事。真实队列协议还需遵守规定的packet格式、内存顺序、完成信号和资源生命周期。

教学上的因果顺序:
准备参数与工作内容 → 按协议发布 → 通知可消费 → 观察完成
这不是可复制的HSA packet写入配方。

例二把“分配失败、产物缺失、答案错误”映射到不同排查入口。应用仍负责检查所有返回值并保存上下文。不能看到设备端失败就一律说“driver bug”,也不能因为API在用户态返回,就断言内核没有参与其资源准备或异常路径。

用反事实问题检查理解

如果kernel根本没有出现在trace里,优先确认编译产物、设备选择、提交状态与过滤条件;如果kernel已经完成,但最后一个元素错误,索引和边界比驱动更值得先查。如果同一输入在某版本失败,另一个版本成功,先固定硬件、依赖、flags和最小复现,再判断是否是实现差异。

最好的排查记录包括具体调用、错误码、输入规模、版本、目标架构和最早失败点。不要只保存一张“执行失败”截图。应用层的错误处理越清楚,向runtime或driver维护者报告的问题越有价值。

本章怎样连接岗位

Runtime岗位关注派发、并发资源、依赖、生命周期和故障传播;driver岗位还需要系统调用、虚拟内存、设备管理及内核调试实践;compiler岗位关注如何产生合法且有效率的代码。认识软件层次是共同前置,阅读一张图却不等于实现过这些层。当前练习只要求你能沿一次请求提出具体问题,不要求直接改内核。

公开ROCr介绍与Linux驱动文档各自解释不同边界。阅读时记录你研究的是正常稳态派发、初始化还是异常处理;同一句“驱动是否参与”在这些场景中的答案可能不同,不能用一个口号覆盖全部路径。

观察 · 推演

任务17的发布因果

payload0readyfalse01 / 03 · PIPELINEpayload0readyfalse01 / 03 · PIPELINE
空槽

消费者不能读取。

1 / 3
阅读完整推演文字
  1. 空槽

    payload:0;ready:false

    消费者不能读取。

  2. 准备

    payload:17;ready:false

    内容17已写,尚未发布。

  3. 消费

    ready:true;consumer:17

    发布后消费17。

跟着例子,走完一遍

例题 01C++20 · 本机可运行

先写内容,再发布

一格队列初始空;提交任务值17。

  1. 填任务内容
  2. 标ready
  3. 消费者检查后读取
35-a.cpp
下载
// Original CPU teaching model; not a device simulator or benchmark.
#include <iostream>
#include <stdexcept>

void require(bool ok) { if (!ok) throw std::runtime_error("model check failed"); }

int main() {
 struct Slot { int payload=0; bool ready=false; } slot;
 slot.payload=17; slot.ready=true;
 require(slot.ready); const int consumed=slot.payload; slot.ready=false;
 require(consumed==17 && !slot.ready);
 std::cout<<"consumed="<<consumed<<'\n';
}

如何编译和运行下载的 .cpp 文件 →

结果与解释

消费者读取17。

模型解释发布顺序,不实现并发或HSA协议。

在本机运行这个例子

下载后,在文件所在目录执行。需要支持 C++20 的编译器;POSIX 示例还需要章节说明中的系统条件。

clang++ -std=c++20 -Wall -Wextra -Wpedantic -Werror -pthread 35-a.cpp -o example && ./example

预期标准输出:

consumed=17
例题 02C++20 · 本机可运行

故障定位入口

分类 allocation、code_object、wrong_value 三类问题。

  1. 资源错误查API与环境
  2. 产物错误查target与装载
  3. 数值错误查oracle和索引
35-b.cpp
下载
// Original CPU teaching model; not a device simulator or benchmark.
#include <iostream>
#include <map>
#include <string>
#include <stdexcept>

void require(bool ok) { if (!ok) throw std::runtime_error("model check failed"); }

int main() {
 const std::map<std::string,std::string> next{
  {"allocation","API/environment"},{"code_object","build/target"},{"wrong_value","indexing/oracle"}};
 require(next.at("wrong_value")=="indexing/oracle");
 for(const auto& [kind,path]:next) std::cout<<kind<<": "<<path<<'\n';
}
结果与解释

打印三个不同排查方向。

分类缩小下一步,不自动判定根因。

在本机运行这个例子

下载后,在文件所在目录执行。需要支持 C++20 的编译器;POSIX 示例还需要章节说明中的系统条件。

clang++ -std=c++20 -Wall -Wextra -Wpedantic -Werror -pthread 35-b.cpp -o example && ./example

预期标准输出:

allocation: API/environment
code_object: build/target
wrong_value: indexing/oracle
常见错误

把所有错误叫driver bug

API涉及设备就直接归因内核。

修正思路:先按产物、资源、派发、执行、数值分层检查最早失败点。

轮到你动手

先写预测或代码,再按需打开提示。完整答案用于对照自己的推理。

练习 1

把分配、优化、发出kernel、缺页恢复、结果比较分别归到路径。

给我一点提示
  1. 编译产生代码
  2. 执行路径仍包括系统支持
查看答案与推理

优化属于编译;分配、派发、缺页恢复属于执行相关活动,涉及不同软件层;结果比较通常由应用完成。不是简单的一项对应一层永久规则。

练习 2

设计一条可交给维护者的最小错误记录。

给我一点提示
  1. 保存最早失败调用
  2. 排除不必要业务代码
查看答案与推理

记录硬件、OS、runtime/compiler版本、flags、最小输入、精确命令、错误码和同步位置,附预期与实际。先验证错误可重复,再推断层次;不得仅附一句GPU崩了。

把理解说出来

先用中文讲清因果,再用英文回答。问题依据技能主题编写,并非公司内部题库。

解释

LLVM与ROCr在同一请求中职责相同吗?

参考回答 / English answer

不同。前者参与编译及目标代码生成,后者提供运行时资源与派发等接口。

Compilation produces code for a target. The runtime manages execution resources and dispatch.
追问

用户态队列意味着驱动永远不参与吗?

参考回答 / English answer

不是。稳态提交方式不能排除初始化、内存管理、调度、缺页或恢复中的驱动职责。

A user-mode queue does not eliminate the driver. Setup, memory management, scheduling, and recovery still require system support.
调试

kernel没出现在trace里,能判为未执行吗?

参考回答 / English answer

还不能。确认trace范围、过滤、采集配置,再查提交状态和产物。

I first check tracing scope and filters. Missing trace data alone does not prove that the kernel never ran.
预测

发布ready再填payload,逻辑上哪里错?

参考回答 / English answer

消费者可能先看到ready,再读到旧内容;真实并发还需正确内存序。

The consumer may observe readiness before valid data exists. A concurrent implementation also needs the correct memory-order protocol.
调试

最后一个元素错,先改驱动合理吗?

参考回答 / English answer

先检查索引、尾部处理、复制长度和oracle,用最小复现缩小问题。

I start with indexing, tail handling, transfers, and the oracle. A minimal reproducer is more useful than changing the driver.
解释

怎样描述自己的ROCm学习范围?

参考回答 / English answer

可以说能解释编译/执行路径与定位策略;没有真实实现就不声称driver或runtime开发经验。

I can explain the stack and a debugging strategy. That is different from having implemented a driver or runtime.

继续查证

公开资料用于查证;本章图解和例题是独立教学内容。CPU 逻辑模型不能证明设备性能。

接着看已有的图解

这些资料按主题补充本章内容。