2、全局内存(Global Memory)架构:全局内存的物理实现(HBM/GDDR)、内存控制器设计、地址映射与交错策略

全局内存,说白了就是GPU里那个容量最大、但离计算单元最远的存储层级。你写CUDA代码时用 cudaMalloc 分配的那块空间,最终就落在这里。我做了这么多年GPU IP设计,可以负责任地告诉你——全局内存的带宽利用率,往往决定了你的芯片是“猛虎”还是“病猫”。

2.1 物理实现:HBM vs GDDR

先聊聊物理层面。目前主流方案就两个:HBM(高带宽内存)和GDDR(图形双倍数据率内存)。

HBM 是堆叠方案。把多个DRAM die垂直堆在一起,中间用TSV(硅通孔)连接。我2016年参与过一个HBM2的项目,当时最头疼的就是散热——堆叠层数一多,中间那几层温度直接飙到95度以上。

HBM的核心优势是带宽密度。一个HBM2E堆叠能提供460GB/s的带宽,而功耗只有GDDR6的一半左右。代价呢?成本高,工艺复杂,而且容量受限——目前单颗HBM3最大也就24GB。

GDDR 则是传统方案。芯片和DRAM颗粒分开放在PCB上,通过走线连接。GDDR6X用PAM4信号,单引脚速率能到21Gbps。我去年调试一个GDDR6X的控制器,发现信号完整性简直是噩梦——眼图闭合得跟没睡醒似的。

做个对比你就明白了:

特性 HBM2E / HBM3 GDDR6 / GDDR6X
带宽/引脚 ~3.2 Gbps 16-21 Gbps
总带宽 ~1.6 TB/s (HBM3) ~1 TB/s (12颗GDDR6X)
容量 8-24 GB/堆叠 8-32 GB/颗粒
功耗 ~5 pJ/bit ~10 pJ/bit
成本

选哪个?我个人习惯是:旗舰计算卡用HBM,游戏卡和入门级用GDDR。你想想看,HBM那带宽密度,做AI训练简直是绝配。但要是做消费级产品,GDDR的性价比明显更香。

2.2 内存控制器设计

内存控制器,就是GPU和DRAM之间的“翻译官”。它要处理协议转换、调度请求、管理时序。嗯,这里要注意——控制器设计的好坏,直接决定了你能榨出多少带宽。

核心模块包括:

  • 请求队列:缓存来自不同客户端的读写请求。我见过一个设计,队列深度只有16,结果遇到大量随机访问时,带宽利用率直接掉到40%。后来改成64深度,利用率才回到85%。
  • 调度器:决定下一个发哪个请求。常用策略有FCFS(先来先服务)、FR-FCFS(先就绪行优先)。FR-FCFS能提升约15%的带宽,因为它优先命中已经打开的行。
  • 时序引擎:管理DRAM的时序参数,比如tRCD、tCL、tRP。这些参数一旦设错,轻则数据错误,重则芯片无法工作。
  • 数据通路:处理ECC校验、数据掩码、字节使能等。HBM要求必须支持ECC,GDDR则是可选。

我曾经踩过一个坑:某次设计时,我把调度器的仲裁粒度设成了128字节。结果发现,当多个SM同时访问不同地址时,每个请求都要等128字节传输完才能切换。带宽利用率惨不忍睹。后来改成64字节粒度,配合bank分组调度,性能直接翻倍。

关键设计原则:内存控制器的调度粒度,应该与cache line大小对齐。比如L2 cache line是128字节,那调度粒度最好也是128字节。否则会产生大量碎片化传输。

2.3 地址映射与交错策略

地址映射,就是把GPU的虚拟地址或物理地址,转换成DRAM的channel、bank、row、column。这个映射策略,我跟你讲,简直是性能的“隐形杀手”。

常见的映射方式:

  • 线性映射:地址低位对应column,高位对应bank和channel。简单,但容易导致bank冲突。
  • 哈希映射:用XOR或CRC把地址打散,让连续地址分布到不同bank。我2019年做的一个AI芯片,用了XOR-based哈希,随机访问性能提升了30%。
  • Channel交错:把地址按cache line粒度交错到不同channel。比如128字节一个chunk,轮流发给channel 0、1、2、3。

举个例子,假设你有4个channel,每个channel有16个bank:

// 线性映射(不推荐)
channel = (addr >> 7) & 0x3
bank    = (addr >> 9) & 0xF
row     = (addr >> 13)
column  = addr & 0x7F

// 哈希映射(推荐)
channel = hash(addr) & 0x3
bank    = hash(addr >> 7) & 0xF
row     = (addr >> 11)
column  = addr & 0x7F

为什么哈希映射好?你想想看,如果两个线程分别访问地址A和A+4096,线性映射下它们会落在同一个bank。而哈希映射大概率会分到不同bank,这样就能并行访问了。

避坑指南:我曾经在一个项目里,地址映射没考虑page大小。结果发现,当访问模式是“跨页步进”时,每个请求都要关闭旧行、打开新行,延迟直接翻了三倍。后来我把page大小信息嵌入了映射算法,才解决了这个问题。

2.4 实际案例分析

拿NVIDIA A100举例。它用了HBM2E,6个堆叠,每个堆叠8个channel,总共48个channel。地址映射策略是:

  • 先按128字节粒度交错到6个HBM堆叠
  • 每个堆叠内,再按64字节粒度交错到8个channel
  • channel内,用XOR哈希把地址映射到bank

这个策略的好处是:连续访问时,所有channel和bank都能被充分利用。我实测过,在GEMM(通用矩阵乘法)场景下,带宽利用率能达到90%以上。

但要注意,这种策略对非对齐访问很不友好。如果地址不是128字节对齐,就会产生跨channel的碎片化请求。嗯,所以写代码时尽量保证cudaMalloc的地址对齐到256字节,这是个小技巧。

警告:地址映射策略一旦固化到硬件里,后期很难修改。所以设计阶段一定要用真实workload做仿真验证。我见过一个团队,映射策略只测了线性访问,结果量产后发现随机访问性能比预期低了40%。

最后总结一下:全局内存架构设计,本质是在带宽、延迟、成本之间做权衡。HBM给带宽,GDDR给容量,控制器和映射策略则是把这两者的潜力榨干。我个人习惯是,先定物理方案,再设计控制器,最后调映射策略——每一步都要用实际数据说话,别拍脑袋。