懒猫后花园

哦,这该死的代码

DDR 的带宽怎么算?介绍 DDR 的简化性能模型

因为 DDR 建模十分常用,却又蛮复杂的很多概念都经常忘记,整理一个笔记记录一下。

一言以蔽之,DDR 是一个 状态有关、高度并发、并带有动态性 的模型。

  • 状态有关: DDR 的状态相关来自于短周期窗口内的地址访问规律,DDR 的访问效果不满足交换律。所以这里存在对相同的访问请求变更顺序和时间油画的空间,DDR 并不会主要做这件事情,片内的 memory controller/ 合并访存 承担了一部分将原始地址访问调整为 DDR 更为友好顺序的功能;
  • 高度并发: DDR 内部存在许多空间上的并发架构,之间会产生可以并行关系,比如 bank parallism、burst read 打满流水线;也存在竞争关系,比如 bank conflict、row miss 、命令流和数据总线占用;
  • 随机动态性: 主要来自 DDR 的刷新特性。时间参数取决于工艺和温度。动态性主要来自于两方面,一是温控,超过 85℃刷新速率翻倍;二是 DDR5 加入的 row hammer 防护,通过 count ACT 命令触发阈值自动触发刷新。如果建模足够细也可以当作确定性模型,但或许是高度状态相关导致确定性模型计算量爆炸,一般当作非确定性模型处理?

DDR 的架构层次和数据粒度

常见的 DRAM 主要以下两种,用金手指插条的 DIMM 和直接焊在芯片外面一圈的 GDDR。

../Extra/Images/IMG_20260806144017156.png

以下是 DIMM 的结构图,层次上,自底向顶分别是 Row-Bank-Chip-Rank-DIMM, 数据接口上,宽度称为 channel width,但 channel width 物理上暴露出来的数据传输宽度,考虑 DDR 性能模型,还有 burst width 、 row buffer capacity 等。对于 DIMM 芯片内外,保持流平衡公式 \(Freq\times Width = Const\) ,IO 频率更高而内部 DDR 内部频率更低,所以在外部的宽度(channel width)较低(32bit/64bit 等),所以在内部的宽度(burst width)更高(8BL 代表一次读写8拍)。而 row buffer 的容量会比 burst width 更大,比如可以容纳 16 burst width 。如果是 GDDR 则 rank 之间不是走共享数据总线,而是分布式多个 channel ,channel 的粒度更细一点。

../Extra/Images/IMG_20260806144017435.png

命令接口上,DDR 的命令并不是 read / write 两种原语,而是包括读写在内的:PRE、ACT、READ、WRITE 四种基础原语,每个原语都带有具体的空间坐标,比如具体哪个 bank 、哪个 row、哪个地址。READ 和 WRITE 一次指令交互的粒度是 burst width ,虽然可以通过配置寄存器切换 busrt width 长度,但 burst width 的宽度是由于内部的空间并行度,实际物理交互只是将对应阵列置无效并不能提高速度。

DDR4 8B channel width,BL8 = 64B,对应 BF16(2B) 数据精度相当于 32 个连续排列的数据,常见数据地址序一般将 channel 维度放在低地址,如果不是到 channel-wise 粒度稀疏一般都可以满足。换句话说,系统的最小友好粒度为各个组件最小友好粒度的最大值,而 channel-wise 粒度稀疏对 DDR 访存并不友好。

DDR 时序参数

DDR 内部有很多时间参数,完整 JEDEC DDR4 有三四十个时序参数,如下是 Ramulator 2[1] 中 DDR4 20 个时序建模参数(取值为 DDR4-2400R 预设的周期数,1 cyc = 0.833 ns):

参数 2400R 取值
单访存延迟 tCL(CAS 列读延迟) 16
单访存延迟 tRCD(行激活到列访问延迟) 16
单访存延迟 tRP(预充电延迟) 16
单访存延迟 tRAS(行最小开启时间) 39
单访存延迟 tRC(行周期,同 bank 两次 ACT 最小间隔) 55
单访存延迟 tRTP(读到预充电间隔) 9
总线占用/转向 nBL(突发长度占用总线周期数,BL8/2) 4
总线占用/转向 tCCD_S(列到列延迟,不同 bank group) 4
总线占用/转向 tCCD_L(列到列延迟,同 bank group) 6
总线占用/转向 tCWL(CAS 写延迟) 12
总线占用/转向 tWR(写恢复时间) 18
总线占用/转向 tWTR_S(写到读转向,不同 bank group) 3
总线占用/转向 tWTR_L(写到读转向,同 bank group) 9
激活限流 tRRD_S(行激活到行激活,不同 bank group) 4
激活限流 tRRD_L(行激活到行激活,同 bank group) 6
激活限流 tFAW(四激活窗口,窗口内最多 4 次 ACT) 26
刷新 tREFI(刷新间隔,7.8 µs) 9361
刷新 tRFC(一次全 bank 刷新占用时间,360 ns) 433
其它 tCS(片选建立时间) 2

模式切换相关参数包括(JEDEC 有、Ramulator 未建模)tXP/tXSR(断电进出)、tCKE、tMOD/tMRD(模式寄存器)、tZQCS/tZQinit(阻抗校准)、tDLLK(DLL 锁定)等。

在不同的地址请求下 DDR 延时建模不一致,比如如果 row miss rate 太高,每次访问都需要重新激活 ACT 指令,此时会受激活限流 tFAW 限制。在一般场景下,最关键的主要由 tRP / tRCD / tCL 三个决定,并且基本在所有协议中,这三个时间参数都几乎相等。在时间上,其主要反映两个因素,row buffer 的变化时间以及多级流水线的延时;在空间上,ACT / PRE / READ / WRITE 的粒度可以看作对于 bank 而言,对于多个 bank 可以做到并发掩盖延时。

这里似乎存在一个聚类工作的可能性,提出一种对地址流请求聚类的方法,分类到不同的解析模型和参数 bottleneck。可以调研常用的 DDR benchmark 是怎么设置的。

Intra-bank 掩盖: Row Buffer 的默认电位是 1/2 VDD,从默认电位到特殊电位有两个状态过程,“默认->特殊”(ACT,tRCD),以及“特殊->默认”(PRE, tRP)。对于同一个 row 的相邻访问。

../Extra/Images/IMG_20260806144017577.png

掩盖 tCL: 每次读写完之后经过 IO 等的 tCL 时间(READ/WRITE,tCL),这来自接口多级流水延时,如果提供足够多的并行 (burst read/write) ,则可以掩盖这个延时,由于 read /write 分析默认以 burst width 交互粒度, 。

Inter-bank 掩盖 tRP/tRCD: 以及为了掩盖 tRP/tRCD ,多个 bank 的达成流水轮流使用总线,掩盖激活时间。

性能上下限解析模型

前面介绍了建模,这里补充具体实验展现不同地址请求对性能的影响,以 ramulator2 DDR4 2400 作为 baseline。首先定一下上下限,DDR 的带宽位于完全串行延时(串行)到完全并行延时(并行)之间,访存地址顺序决定了时空掩盖延时关系,在这之间进行取值。

\(B=\frac{Burst \times f}{n}=\frac{B_{ideal}}{n}=\frac{64B\times 1.2GHz}{n}=\frac{76.8GB/s}{n}\) ,其中 \(Burst\) 是一次 burst 交互的长度,BL8 代表一次 burst 交互长度为 8,而 channel width 是 64bit=8B,所以一次 burst 交互数据宽度是 64B;\(n\) 代表一次交互需要的周期数,我们建模地址请求主要影响该参数。

在不同场景下各个访存

上限,每次读取都可以隐藏延时(bank interleave+ row hit),只受到总线占用的限制,因此:

\[B_{UB}=\frac{B_{ideal}}{nBL}=\frac{B_{ideal}}{4}=19.2GB/s \]

下限,准确的解析下限很难定义给出,这里给出几种下限定义:

  • bank interleave + row conflict;
  • bank conflict + row hit;
  • bank conflict + row conflict。

case 1:bank interleave + row conflict。 这里假设每个请求都是 row conflict ,则受到 ACT 限流 tRRD_L, tFAW 限制,因此:

\[\begin{align} n_{max}=\max(nBL, tRRD\_L, \frac{tFAW}{4})=6.5\\ B_{LB}=\frac{B_{ideal}}{6.5}=11.8GB/s \end{align} \]

定义带宽利用率为实际带宽与最高带宽的比值, \(Util=\frac{nBL}{n_{max}}=\frac{4}{6.5}\approx 62.5\%\) ,row conflict 情况能够维持大概 6 成带宽,实际由于刷新等其他非理想因素可能会比这个更低。

case 2:bank conflict + row hit。 所有请求钉在同一个 bank、但都命中已打开的行:不需要再付 ACT/PRE,唯一约束是同 bank(必然同 bank group)连续两次读的最小间隔 tCCD_L,因此:

\[\begin{align} n=\max(nBL, tCCD\_L)=\max(4,6)=6\\ B=\frac{76.8GB/s}{6}=12.8GB/s,\quad Util=\frac{4}{6}\approx 66.7\% \end{align} \]

case 3:bank conflict + row conflict。 同一个 bank 且每个请求都换行:PRE、ACT、RD 在同一 bank 上完全串行,每个请求付一整个行周期 tRC,因此:

\[\begin{align} n=tRC=tRAS+tRP=39+16=55\\ B=\frac{76.8GB/s}{55}\approx 1.40GB/s,\quad Util=\frac{4}{55}\approx 7.3\% \end{align} \]

Ramulator 2 不同 DDR 配置的解析上限与下限利用率如下。DDR5 的 BL16 让单次请求占总线 8 cyc,而 tFAW/4 = 8 恰好允许同样的激活速率,激活限流与总线占用打成平手,纯 random 流理论上压不垮 DDR5;LPDDR5/HBM2 的突发短(nBL=2),激活限流相对更紧,下限只有 5 成左右。看起来利用率上下限优化和具体协议非常相关,这些参数和什么设计因素有关,以及具体实践和理论是否一致还需要进一步分析调研。

配置 通道数 单通道峰值 (GB/s) 总峰值 (GB/s) nBL n_max 下限利用率 (nBL/n_max)
DDR4-2400 1 19.2 19.2 4 6.50 61.5%
DDR4-2400 4 19.2 76.8 4 6.50 61.5%
DDR5-3200 1 25.6 25.6 8 8.00 100.0%
LPDDR5-6400 1 12.8 12.8 2 4.00 50.0%
HBM2-2Gbps 4 32.0 128.0 2 3.75 53.3%

上下限何时取到?

在 ramulator2 DDR4 下,地址从低到高按 Channel → Column → Bank → Row 切分顺序映射(RoBaRaCoCh),生成 8192 个请求,根据生成模式(顺序生成/随机生成/随机生成但单调),生成的地址范围(8192 个请求,覆盖 512KiB 地址空间100%,覆盖 256MiB 空间 0.195%)以及是否刷新,不同实验结果如下。这一套实验配置相当于 bypass memory controller 直接操作 DDR 观察地址请求设置对带宽的影响。如前文,row 命中情况存在 row hit/ row miss/ row conflict 三种情况,所以 row hit + row conflict 不等于 1。bank_ 前缀的实验把地址限制在同一个 bank 内(该 bank 的地址是每 128KiB 重复一段 8KiB 的不连续碎片),用于测量 bank conflict 场景。每个指标格内为 default/norefresh 两个值。

实验名称 想说明什么 生成模式 地址范围 发射顺序 cyc/req GB/s 利用率 row hit row conflict
seq row hit + bank interleave 的顺序流(上限参照,实测受 tCCD_L 限制) sequential 连续 顺序 5.276 / 5.111 14.56 / 15.03 75.8% / 78.3% 0.991 / 0.992 0.000 / 0.006
random_256MiB case 1:bank interleave + row conflict(多 bank 解析下限) random 256 MiB 随机 6.760 / 6.468 11.36 / 11.87 59.2% / 61.8% 0.001 / 0.001 0.993 / 0.997
random_256MiB_sorted 大范围下排序:单调不等于局部性(间距 32KiB ≫ 行) random 256 MiB 排序 6.094 / 5.801 12.60 / 13.24 65.6% / 69.0% 0.107 / 0.107 0.884 / 0.888
random_512KiB 稠密随机铺满 bank group(tCCD_S),实测最高利用率 random 512 KiB 随机 4.474 / 4.302 17.16 / 17.85 89.4% / 93.0% 0.342 / 0.342 0.616 / 0.617
random_512KiB_sorted 稠密排序 ≡ 顺序流(对照组,与 seq 逐值相等) random 512 KiB 排序 5.276 / 5.111 14.56 / 15.03 75.8% / 78.3% 0.991 / 0.992 0.000 / 0.006
bank_random_256MiB case 3:bank conflict + row conflict(真正下限,≈ tRC=55 串行) random 256 MiB 单 bank 随机 56.482 / 53.892 1.36 / 1.43 7.1% / 7.4% 0.016 / 0.016 0.980 / 0.984
bank_random_256MiB_sorted 单 bank 下排序把 ~4 个请求攒进同一行,摊薄 tRC(3.5×) random 256 MiB 单 bank 排序 15.924 / 15.180 4.82 / 5.06 25.1% / 26.3% 0.753 / 0.755 0.245 / 0.245
bank_random_512KiB bank conflict + 高命中:bank 间并行 > 行局部性的反例 random 512 KiB 单 bank 随机 7.929 / 7.601 9.69 / 10.10 50.4% / 52.6% 0.953 / 0.953 0.046 / 0.047
bank_random_512KiB_sorted case 2:bank conflict + row hit(tCCD_L 界 n=6) random 512 KiB 单 bank 排序 6.276 / 5.990 12.24 / 12.82 63.7% / 66.8% 0.999 / 1.000 0.000 / 0.000

可以看到顺序读写(~75%)并不一定是利用率最高,最高利用率在 随机+小范围地址空间内达到 (90%),随机单调+小范围利用率退化也证明了这一点(75.8%)。这是因为顺序读写虽然都是 row hit (row hit ~99%),但受到同 bank 制约(tCCD_L),而随机+小范围地址访问则受 tCCD_S 影响,虽然现在的 RoBaRaCoCh 映射方案顺序访问是优先填满 row 然后切换 bank 也实现了 bank interleave (row hit 优先级> bank interleave 优先级),但将 bank 映射到更低地址利用率会更高(bank interleave 优先级 > row hit 优先级),这个由 tCCD_L 和 tCCD_S 的大小关系决定。但无论怎样 利用率上限在 row hit + bank interleave 的情况下达到,具体实现可以通过静态地址映射方案+动态 MC 调度实现。而多 bank 下的利用率下限在随机+大范围 case 出现,不考虑刷新约为 60%,和前面解析下限近似略低;若进一步把地址囚禁到单个 bank(bank_ 组),下限下探到 7.3%(≈ tRC 串行),与解析 case 3 一致。


  1. https://arxiv.org/abs/2308.11030 ↩︎

posted @ 2026-08-06 14:40  DevilXXL  阅读(20)  评论(0)    收藏  举报