Adreno GPU架构深潜:A6xx与A7xx在Threadgroup Memory上的本质区别与演进
在移动端 GPU 架构中,Threadgroup Memory(在 Vulkan 中称为 Shared Memory,在 OpenCL 中称为 Local Memory,在硬件层面通常对应 Local Data Share, LDS)是决定计算着色器(Compute Shader)和现代渲染管线(如 Mesh Shader)性能的关键基础设施。
高通 Adreno 系列 GPU 在经历 A6xx(如 Adreno 630/640/650/660)向 A7xx(如 Adreno 730/740/750)的跨代演进中,其 Threadgroup Memory 硬件架构、数据通路以及与寄存器文件的交互机制发生了本质性的变革。本文将深度剖析这两代 GPU 在 Threadgroup Memory 设计上的核心底层区别。
一、 物理拓扑与 SP 内部布局的重构
要理解 Threadgroup Memory 的变化,首先需要理解 Shader Processor (SP) 物理布局的演进。
1. A6xx 的 “SP-Centric” 绑定设计
在 A6xx 架构中,每个 SP 内部封装了独立的、物理上紧耦合的 LDS 块(通常为 64KB)。
- 物理位置:LDS 紧邻 SP 的向量寄存器文件(VRF)和算术逻辑单元(ALU)。
- 调度局限:这种设计属于典型的“静态绑定”。一个 Threadgroup 只能被调度到单个 SP 上执行。如果一个 Compute Kernel 申请了 32KB 的 Threadgroup Memory,即使该 SP 的计算资源未满载,由于物理 LDS 容量的限制,其活动 Wavefront(在 Adreno 中称为 Fiber Group)的数量(Active Warps/Waves)也会受到严格限制,从而导致延迟隐藏能力下降。
[A6xx SP Layout]
+------------------------------------------+
| SP (Shader Processor) |
| +----------------+ +----------------+ |
| | ALU Pipelines | | Vector Regs | |
| +--------+-------+ +--------+-------+ |
| | | |
| +--------+-------------------+--------+ |
| | Local Data Share (LDS) - 64KB | |
| +-------------------------------------+ |
+------------------------------------------+
2. A7xx 的 “Clustered / Pooled” 弹性设计
A7xx 引入了更为现代的处理器集群(Processor Cluster)概念,对 SP 内部的存储资源进行了“池化”与“解耦”重构。
- 物理位置:LDS 不再是单片紧耦合在单个 SP 微架构内部,而是作为高速、低延迟、多通道的共享 SRAM 阵列,放置在 SP 簇的中央。
- 容量提升:A7xx 的旗舰型号(如 Adreno 740/750)将每个计算集群可访问的 Threadgroup Memory 物理容量提升至 128KB,并且支持动态、高粒度的分区。
- 弹性调度:多个 SP 子单元可以更灵活地共享和映射同一块物理 Threadgroup Memory。这种设计允许编译器和硬件调度器更自由地平衡寄存器压力(Register Pressure)与 Threadgroup Memory 占用,显著提升了 GPU 的占用率(Occupancy)。
二、 Bank 架构与冲突解决机制的升级
Threadgroup Memory 通常被划分为多个物理 Bank,以便并发访问。一旦多个 Fiber 访问同一个 Bank 的不同地址,就会发生 Bank Conflict(冲突),导致访问被串行化,严重拖慢执行效率。
| 维度 | Adreno A6xx | Adreno A7xx |
|---|---|---|
| Bank 数量 | 通常为 32 个 Bank | 升级为 32 或 64 个高带宽 Bank |
| Bank 宽度 | 32-bit (4 Bytes / Dword) | 32-bit (优化了 16-bit FP16 动态合并) |
| 交叉开关 (Crossbar) | 128-bit 宽度总线,多周期分发 | 256-bit 或宽总线,单周期多路路由 |
| 冲突延迟处罚 | 高(序列化开销大,缺乏片上快速旁路) | 低(引入了更先进的冲突检测与广播合并网络) |
1. A6xx 的 Bank 访问特征
在 A6xx 上,当 Fiber Group(通常以 Wave64 或 Wave32 执行)发起 Threadgroup Memory 读写时:
- 如果 32 个 Fiber 恰好映射到 32 个不同的 Bank,可以达到单周期吞吐。
- 一旦发生 Bank Conflict,A6xx 的内部纠错和重试机制(Arbitrator)需要多个时钟周期来串行解决。
- FP16 劣势:在处理半精度(FP16/half)数据时,A6xx 的 Bank 寻址对齐相对生硬,无法高效地将两个 16-bit 的相邻访问合并(Coalesce)为单个 32-bit Bank 端口的并发写入,造成不必要的冲突和带宽浪费。
2. A7xx 的硬件级优化
A7xx 重新设计了 Crossbar(交叉开关)和地址译码器:
- 动态广播(Broadcast)与多播(Multicast):如果一个 Wave 内的多个 Fiber 同时读取同一个 Bank 的同一个地址(即常量广播),A7xx 可以在硬件级实现单周期多播分发,不占用额外的 Bank 带宽。
- FP16 紧凑合并:A7xx 能够完美识别连续的 16-bit 线程访问,并将其合并成一个 Dword (32-bit) 的 Bank 访问通道。这使得在移动端广泛使用的端侧 AI、图像处理(Heavy FP16 计算)在访问 Threadgroup Memory 时,带宽利用率直接翻倍。
三、 数据通路(Datapath)与寄存器交互的革命
在 GPU 执行计算任务时,数据通常遵循以下路径:Global Memory (L2/DDR) -> Register File (VRF) -> Threadgroup Memory (LDS) -> Register File (VRF)。
这两代架构在数据通道的连通性和指令阻塞设计上有着本质区别。
[A6xx Datapath]
Global Memory (L2) ---> [VRF (Registers)] <---> [ALU]
^
| (Requires ALU instruction for moving)
v
[LDS Memory]
[A7xx Datapath]
Global Memory (L2) -+-> [VRF (Registers)] <---> [ALU]
| ^
| | (Direct High-Speed Bypass)
| v
+-> [LDS Memory] (Supports Direct Async Copy)
1. A6xx:ALU 参与的显式搬运
在 A6xx 架构中,将数据从 Threadgroup Memory 加载到寄存器(或反之)需要占用 SP 的部分寄存器读写端口,并且在编译器层面,这类 ldlg (Load Local Geometry/Group) 和 stlg (Store Local Geometry/Group) 指令容易与普通的 ALU 向量计算指令产生冲突,导致流水线气泡。
- 同步机制:编译器必须插入显式的等待标记(如
sys_wait或特定的延迟 Token),以确保数据已经写入 LDS,随后才能被其他 Fiber 安全读取。这种纯软件/编译器管理的依赖性极易因编译器估计不足而造成硬件停顿。
2. A7xx:硬件异步拷贝与旁路(Bypass)
A7xx 引入了类似于现代桌面级 GPU(如 NVIDIA Ampere 之后的 LDGSTS)的异步拷贝机制:
- 直接通路(Direct Routing):支持从 L2 Cache / Global Memory 直接将数据加载到 Threadgroup Memory,而无需中途占用宝贵的向量寄存器(VRF)。这不仅极大释放了寄存器压力,还规避了寄存器读写端口的抢占。
- 硬化屏障(Hardware Barrier / Sync):A7xx 在硬件层面集成了更高效的屏障控制器(Barrier Controller)。在 Compute Shader 中调用
GroupMemoryBarrier()时,硬件能够更灵敏、以更低的时钟周期开销挂起和唤醒 Fiber Group,不再完全依赖编译器插入海量的空操作指令(NOPs)来对齐时钟。
四、 原子操作(Atomic Operations)的硬件下放
在并行计算中,对 Threadgroup Memory 进行原子操作(如 atomicAdd)是构建直方图、链表、无锁队列等算法的核心。
A6xx 的软实现:
A6xx 的 LDS 内部缺乏强大的逻辑算术单元。当 Fiber 执行atomicAdd时,硬件必须将 LDS Bank 内的原始数据读取出来,送回 SP 内部的 ALU 进行加法计算,然后再写回 LDS。这构成了一个长延迟的闭环,并且在多线程高并发冲突时,锁总线时间极长,会导致严重的管线阻塞。A7xx 的硬实现(LDS-Side Atomics):
A7xx 将原子操作逻辑单元(Atomic ALUs)直接集成到了 Threadgroup Memory 控制器附近。这意味着,当 Fiber 发起原子加、原子最大值等指令时,指令直接发送给 LDS,由 LDS 内部的专用硬件在 Bank 层面就地完成计算并写回。- 吞吐量提升:整个操作对 SP ALU 来说是“发射后不管”(Fire-and-Forget)的异步行为。这让 A7xx 的本地原子操作吞吐量相比 A6xx 提升了数倍,极大地优化了粒子系统、物理模拟等需要频繁同步的计算负载。
五、 面向开发者的底层优化启示
针对 A6xx 与 A7xx 在 Threadgroup Memory 架构上的上述本质改变,开发者在进行 Vulkan / OpenCL / Unity HLSL 级底层优化时,应当调整其优化策略:
Threadgroup 尺寸选择:
- 在 A6xx 上:应谨慎使用过大的 Threadgroup 尺寸。如果单个 Threadgroup 的 Shared Memory 占用超过 16KB,应尽量重构算法,否则会导致 Occupancy 急剧下降。
- 在 A7xx 上:可以更大胆地利用大容量(如 32KB/64KB)的 Threadgroup Memory,并配合 Wave32 模式执行,利用 A7xx 强大的物理池化和高 Occupancy 调度能力掩盖延迟。
数据类型对齐与合并:
- 在 A7xx 上,强烈建议将 Shared Memory 中的数据结构打包为 16-bit(如
half、uint16_t)。A7xx 的硬件 Bank 合并机制能够将相邻 Fiber 的 16-bit 读写无缝拼装,而不会像 A6xx 那样产生严重的 Bank 冲突。
- 在 A7xx 上,强烈建议将 Shared Memory 中的数据结构打包为 16-bit(如
利用异步屏障:
- 编写 Compute Shader 时,尽量将 Threadgroup 数据的读取(Load)与实际使用(Use)隔开足够长的独立 ALU 指令区间。在 A7xx 上,硬件会自动利用这段区间进行后台异步拷贝,从而实现完美的指令流水线重叠(Pipelining)。
总结
Adreno A6xx 的 Threadgroup Memory 属于一种经典的、紧耦合但相对静态的移动端硬件设计;而 A7xx 则彻底打破了这一温床,转向了池化、宽总线、异步化、具有就地原子计算能力的现代化计算架构。这一演进不仅为移动端光线追踪(Ray Tracing)中的 BVH 遍历加速提供了强力支撑,也让移动端运行复杂的现代 GPU Driven 渲染管线变得更加高效。