自研加速器芯片背景知识补充
补充自研加速器芯片涉及的异构执行模型、验证仿真、性能调试、框架算子、编译器、运行时和驱动概念
host-device 异构计算执行模型
Heterogeneous Computing(异构计算)指 CPU、GPU、NPU、FPGA 等不同处理单元协同工作。host-device 模型是其中最常见的一种,现代 GPU 以及各类自研 AI 芯片基本都是典型的 host-device 异构计算。CPU 作为 host 侧,负责组织、调度、加载、提交和同步;GPU 或 AI 芯片作为 device 侧,负责高吞吐张量计算;runtime、driver 和 firmware 把两边接起来。
执行模型本质上是解决软件如何驱动硬件计算的一套抽象,涵盖程序如何变成目标设备上实际运行的指令的全过程,是 ISA + ABI + OS 调度模型的集合。所谓可编程的功能,也需要通过具体的执行模型来实现。一段高级语言编写的代码要运行在异构计算设备中,执行模型大致如下:
1
2
3
4
5
6
7
8
9
模型代码: PyTorch / vLLM / TensorFlow / JAX / ONNX Runtime
-> 框架把模型拆成 OP / graph
-> 后端选择每个 OP 的执行设备: CPU、CUDA GPU、TPU、NPU 或自研 AI 芯片
-> dispatcher 或编译器决定每个 OP 怎么执行
-> runtime 分配显存、管理 stream/event、提交 kernel
-> driver 负责命令队列、DMA、中断和权限,把命令发送到设备
-> firmware / command processor 调度设备侧任务
-> 矩阵单元、向量单元、DMA、SRAM、HBM/DRAM 完成实际计算
结果回到框架 tensor
从微观上看,运行一个 host 程序的过程大致如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
-> host程序启动
-> 初始化runtime
-> runtime调用driver打开设备
-> 创建context/stream/queque
-> 加载目标平台binary
-> 分配host/device内存
-> 拷贝数据到device可访问内存
-> 配置kernel参数
-> 提交kernel launch命令
-> driver写入命令队列
-> 目标硬件调度并执行binary中的指令
-> 执行完成后driver/runtime同步
-> 拷贝结果回host
-> 释放资源
验证和仿真
整个芯片不同的抽象层级大致可以分为:系统应用 Application–> 软件 Software–> 操作系统 OS–> 固件 Firmware–> ESL Model(SystemC / C++ / HLS)–> RTL(Verilog / VHDL / SystemVerilog)–> Gate Level(门级网表)–> Layout(版图)。
加速器芯片研发过程中一大工作量,就是在各种仿真环境中进行验证,完成代码在自研芯片上的功能测试和性能测试。仿真环境分为纯 CPU 进行逻辑上的仿真,以及进行 RTL 电路级别的仿真两种。
逻辑仿真中一大重要的方法学是 ESL(Electronic System Level,电子系统级)设计方法学,是 RTL 之上的一个抽象层级。ESL Model 是一种高抽象度的功能模型,用于在芯片设计的早期验证系统架构,而不描述具体电路。比如 Xilinx 的 HLS 就是 ESL 设计方法中的一种实现方式。ESL Model 具体又分 Function Model、Arch Model 和 Performance Model。Function Model 抽象层级最高,只关心输出是否正确;Arch Model 关注抽象的硬件结构,用于做架构探索;Performance Model 关注架构的性能
RTL 仿真分 Simulator 和 Emulator 两种,两种仿真环境都可以进行波形抓取来进行细粒度的问题定位和性能分析,区别在于前者是用软件解释或编译 RTL 来执行,后者是将 RTL 映射到专用硬件中执行。Simulator 的主流运行平台包括 Synopsys VCS、Cadence Xcelium 和 Siemens Questa;Emulator 的主流运行平台包括 Cadence Palladium、Synopsys ZeBu 和上海合见的 UVHS。Simulator 更灵活更方便,可以查看任意多的波形;Emulator 更快,比 Simulator 快出几个数量级,但价格也比 Simulator 贵出几个数量级。另外 Emulator 本质上也是在 FPGA 上运行,但相较于做 Prototype 的通用 FPGA,调试能力更强,更适合做大规模 SoC 的系统级验证工作。
性能分析和调试工具
Profiler 和调试工具也是一大工作量。没有 timeline、kernel trace、hardware counter、内存带宽统计、算力利用率、stall reason、graph dump、IR dump 和数值 diff 工具,芯片很难被客户稳定使用。一次慢运行必须能还原到具体 OP、kernel、layout、DMA、同步点或硬件模块。
算术强度(arithmetic intensity)指的是每搬运一字节数据所做的计算量,FLOP/Byte。
框架、算子和 kernel
框架接入是用户可见的入口,通过写 torch.tensor(..., device="my_accel")、model.to("my_accel"),或通过推理引擎,可以在目标设备上运行模型。Dispatcher 是框架内部的分派机制,用来根据 OP、dtype、device、layout 等信息选择具体后端实现。
OP(operator,算子)是框架语义层的“操作”。用户写的模型会被框架表示成一组操作,例如 matmul、softmax、reshape、attention。graph 是这些 OP 以及数据依赖关系组成的计算图。
kernel 是设备上真正执行的一段程序,由程序员声明并给定具体 shape、dtype、layout、tile 切分和并行策略等参数,在设备上由多个线程并行执行。在不同的目标设备上,kernel 的线程模型、内存层次和指令集可能完全不同。一个 OP 不一定对应一个 kernel。编译器可能把多个 OP 融合成一个 kernel(融合算子),一个 OP 也可能拆成多个 kernel。
OP 和 kernel 中间隔着图优化、layout 选择、tiling、memory planning、kernel selection、autotune 和 runtime 调度等,很多性能问题就藏在这个中间层:尽管语义上只是一个简单 OP,但落到设备上可能产生额外 layout conversion、低利用率 tile、同步等待或 HBM 往返等等。
典型算子可以粗略分几类:
| 类型 | 常见 OP | 主要压力 |
|---|---|---|
| 矩阵计算 | matmul、GEMM、BMM | 矩阵单元吞吐、tile 利用率、HBM 带宽 |
| 逐元素计算 | add、mul、activation、quant、dequant | 向量单元吞吐、访存开销 |
| 归一化 | softmax、RMSNorm、LayerNorm | reduce、特殊函数、数值稳定性、片上缓存 |
| 形状和布局 | reshape、view、transpose、contiguous | layout 表达、是否产生真实拷贝 |
| 注意力 | attention、RoPE、KV cache 访问 | 矩阵计算、softmax、片上存储、显存带宽 |
| 稀疏/路由 | embedding、MoE、expert parallel | 随机访存、通信、负载均衡 |
| 通信 | allreduce、allgather、reduce-scatter、send/recv | 多卡互联带宽、拓扑、同步 |
编译器相关
编译器完成源代码到目标平台二进制文件的转化,是最难的工作之一。编译器前端要接 PyTorch、JAX、TensorFlow 或 ONNX 等框架,中端可能使用 StableHLO、MLIR、XLA 或自研 IR,后端再做 fusion(算子融合)、constant folding、shape specialization、layout optimization、memory planning、tiling、lowering、寄存器分配、指令调度、代码生成和 autotune 等。
编译器会基于底层硬件指令(ISA 操作语义)封装一系列 builtin 函数。程序员编写源代码并调用 builtin 函数来驱动硬件完成计算。Builtin 是编译器或语言内置函数,通常对应特殊硬件操作或低层能力。
ISA(Instruction Set Architecture,指令集架构)定义处理器能听懂哪些指令,规定了有哪些指令,有多少寄存器,每条指令的格式,地址空间,原子操作,内存模型等等,可能围绕矩阵乘、向量计算、load/store、DMA、同步、低精度转换等进行设计。对 AI 芯片来说,ISA 不一定像通用 CPU 那样暴露给普通程序员。它可能更多服务于编译器、手写 kernel 工程师和算子库。
可编程相关
Runtime 是高层运行时库,如 CUDA Runtime API,面向程序员提供了内存分配 malloc、设备之间的内存拷贝 memcpy、kernel 启动、module load、stream 任务队列、event marker 等功能,面向硬件通过调用 Driver 提供的接口实现对硬件的操作。现实路线通常是先做 CUDA-like 的窄 API,保证能分配内存、拷贝数据、加载 ELF、launch kernel 和同步,然后再补 stream、event、graph capture、异步执行和内存池等功能。
Driver 是直接管理硬件的软件,功能包括设备初始化、内存映射、命令队列提交、DMA、中断、上下文切换、binary 装载等。通过 Driver 的接口把指令给到硬件的 control 部分,硬件根据指令中各个位置的信息解码出 ISA 语义上的各个参数(shape、地址、模式等),并 trigger 硬件执行。
Memory Model(内存模型)是针对编程语言的概念,说明了有哪些 memory,并定义不同内存空间、cache、shared memory、SRAM、HBM 和主机内存之间的访问规则、生命周期、同步规则、搬运规则。内存模型描述了编程语言如何抽象和访问内存;存储器硬件只提供线性的地址空间,不同编程语言需要在这个线性空间上建立自己的软件抽象以便于程序员使用。C 抽象为字节序列+指针、Java 抽象为对象+堆、Python 抽象为对象+垃圾回收堆。内存模型的设计初衷是屏蔽硬件差异,保证程序在不同平台之间的可移植性。
C/C++的内存模型如下,在编译生成的 map 文件中可以找到对应的概念。


