如何选择合适的DRAM调度策略?Ramulator 2.1 FRFCFS与Row Policy完整对比
【免费下载链接】ramulator2
Ramulator 2.0 is a modern, modular, extensible, and fast cycle-accurate DRAM simulator. It provides support for agile implementation and evaluation of new memory system designs (e.g., new DRAM standards, emerging RowHammer mitigation techniques). Described in our IEEE CAL 2023 paper: https://arxiv.org/pdf/2308.11030.
Ramulator 2.1 是一款模块化、可扩展、周期精确(cycle-accurate)的 DRAM 模拟器,用于快速实现与评估新的内存系统设计。在它的内存控制器中,DRAM 调度策略由两个正交部件决定:请求调度器(scheduler)决定"下一个发哪条请求",行策略(row_policy)决定"何时关闭打开的行"。选对两者的组合,直接影响模拟出的延迟、带宽与 RowHit 命中率。本文带你完整对比 FRFCFS、FRFCFS-RowHit 调度器与 Open、ClosedCAP 行策略,帮你在几分钟内为实验做出正确选择。
为什么调度策略会决定 DRAM 性能
DRAM 的访问遵循"先开行(ACT)再读列(RD)"的协议。当连续访问落在同一行时,命中行缓冲(RowHit),延迟最低;若访问另一个行,则必须关行再开行(RowConflict),代价最高。调度器和行策略,本质上就是在"让正确的行保持打开"这件事上做权衡:
| 现象 | 含义 | 对性能的影响 |
|---|---|---|
| RowHit | 访问已在打开的行 | 最低延迟,省 ACT |
| RowMiss | 访问未打开的行 | 需 ACT,中等代价 |
| RowConflict | 打开行后又要访问别的行 | 需 PRE+ACT,代价最高 |
理解了这个背景,再看两种调度器就顺理成章了。
Ramulator 2.1 的两大请求调度器
调度器接口 i_scheduler.h 只有一个核心方法get_best_request():从待处理队列里挑出"下一条最值得发出的请求"。它优先选时序就绪的请求,就绪相同时按到达顺序(先到先得)打破平局。
FRFCFS:先到先得,优先就绪
最基础的调度器,实现见 frfcfs.cpp,Python 包装为 frfcfs.py。它只做一件事:在队列里扫描一遍,挑出时序已就绪的请求;若多个就绪,则取到达最早的那个。
- 简单、无额外状态,适合绝大多数基准场景
- 不做"行"维度的前瞻,可能让一条未就绪的旧请求"占着坑"
- 是 Ramulator 官方示例的默认选择(见 example_config.py)
FRFCFS-RowHit:为行命中"让路"
在 FRFCFS 基础上增加了行感知,实现见 frfcfs_rowhit.cpp,Python 包装为 frfcfs_row_hit.py。它分两轮扫描:
- 第一轮:标记出队列里所有"行命中"请求,并记录每个 bank 最早的命中到达时刻;
- 第二轮:执行 FRFCFS,但跳过那些会抢占已有行命中的关行请求(
would_preempt_rowhit)。
直觉:当 nRTP < nCCD_L 时,一旦行被打开,继续访问同行比"抢先关行去访问另一行"更划算。FRFCFS-RowHit 就是防止后者打乱前者。
| 对比项 | FRFCFS | FRFCFS-RowHit |
|---|---|---|
| 核心逻辑 | 就绪优先 + 到达顺序 | 就绪优先 + 保护行命中 |
| 额外状态 | 无 | 每 bank 的命中标志与到达时刻 |
| 适用场景 | 通用基准、基线 | 高局部性负载、想最大化 RowHit |
| 复杂度 | 低 | 略高(两轮扫描) |
Row Policy:Open 与 ClosedCAP 怎么选
行策略接口 i_row_policy.h 定义了 4 个钩子:pre_schedule()(调度前,可注入关行命令)、try_upgrade_command()(选定时可把命令升级为自动预充电版)、on_issue()(命令发出后)、post_schedule()(本 tick 收尾)。
Open 行策略
最宽松的策略:访问后不主动关行,让行一直开着。实现极简,见 open.cpp 与 open.py(所有钩子均为空操作)。
- 局部性好的负载 → RowHit 高、延迟低
- 访问模式跳跃 → 打开行长期占用,可能引发不必要的 RowConflict 与能耗
ClosedCAP 行策略:访问 N 列后自动关行
带"列访问计数上限"的关闭策略,见 closed_cap.cpp 与 closed_cap.py。它给每个 bank 记一个列访问计数器,达到上限cap(默认 4)后主动关行:
- 优先尝试命令升级:把
RD→RDA、WR→WRA(自动预充电,读/写同时关行),这是最省时序的方式; - 若标准不支持 AP 命令或时序未就绪,则回退到注入显式
PREpb关行; - 一旦发出关行命令,该 bank 的计数器立即清零重新计数。
| 对比项 | Open | ClosedCAP |
|---|---|---|
| 关行时机 | 从不主动关 | 列访问达到cap后关 |
| 关行手段 | — | 优先 AP 升级,回退 PREpb |
| 参数 | 无 | cap(默认 4) |
| 适用场景 | 高局部性、求低延迟 | 跳跃访问、平衡能耗与延迟 |
调度策略与行策略如何协同
在 generic_ddr_controller.cpp 的tick()中,两者的协作流程清晰可见:
- 推进时钟、刷新队列统计、排空已完成读请求;
- 刷新管理器与行策略
pre_schedule()先行(ClosedCAP 可能在此注入PREpb); - 按active > priority > read/write的优先级,调用调度器挑出最佳候选;
- 行策略
try_upgrade_command()可能把候选升级为自动预充电命令; - 向 DRAM 发出命令,行策略与插件
on_issue()记录; - 请求退休(
retire)或升级为 active; - 行策略
post_schedule()收尾。
也就是说:调度器选"发谁",行策略在选前、选中后、发出后三个时刻介入,共同决定最终命令序列。改其中任一方,都可能改变 RowHit/Conflict 的统计。
三步上手:在示例中切换调度策略
仓库自带的 examples/example_config.py 是最快的起点。它用SimpleO3前端 + DDR4 + GenericDDR 控制器,默认就是FRFCFS+Open:
ctrl = ramulator.controller.GenericDDR( dram=ddr4, scheduler=ramulator.scheduler.FRFCFS(), # 可换成 FRFCFSRowHit() refresh_manager=ramulator.refresh_manager.AllBank(), row_policy=ramulator.row_policy.Open(), # 可换成 ClosedCAP(cap=4) addr_mapper=ramulator.addr_mapper.RoBaRaCoCh(), )三步即可对比:
- 换调度器:把
FRFCFS()改为FRFCFSRowHit(); - 换行策略:把
Open()改为ClosedCAP(cap=4)(cap可调); - 跑并看指标:执行
python3 examples/example_config.py,从sim.stats里读row_hits/row_misses/row_conflicts/avg_read_latency。
小技巧:同一负载分别用 4 种组合(2 调度器 × 2 行策略)各跑一遍,对比
row_conflicts与avg_read_latency,往往就能看出哪个组合更贴合你的访问模式。
如何验证你的选择:关键指标与测试
sim.stats中memory_system → controller下有几个最直接的计数器:
| 指标 | 含义 |
|---|---|
row_hits/row_misses/row_conflicts | 三类行访问的总数 |
avg_read_latency | 平均读延迟(控制器周期) |
read_queue_len_avg/write_queue_len_avg | 读/写队列平均占用 |
想从"命令序列"层面确认行为是否符合预期,仓库的controller_scheduling测试套件就是范本,见 test_controller_genericddr.py:
test_frfcfs_prefers_ready_request_over_older_blocked_request:验证 FRFCFS 优先发就绪请求;test_frfcfs_rowhit_keeps_same_bank_row_hits_before_conflict_close:验证 FRFCFS-RowHit 在关行前保住同行命中;test_closedcap_upgrades_second_access_to_rda:验证 ClosedCAP 在cap=1时把第二次访问升级为RDA。
运行方式:
PYTHONPATH=python pytest tests/controller_scheduling -q选型速查清单
- 追求基线、通用评估→
FRFCFS+Open(官方示例默认,最简单)。 - 高局部性、想压 RowHit 延迟→
FRFCFS-RowHit+Open。 - 访问跳跃、想控制能耗与 RowConflict→
FRFCFS-RowHit+ClosedCAP(cap=4),并可按负载调小/调大cap。 - 想确认行为正确→ 用
controller_scheduling套件断言命令序列与gap。
小结
在 Ramulator 2.1 里,DRAM 调度策略 =调度器+行策略的组合。FRFCFS简单稳健,FRFCFS-RowHit在行局部性上更聪明;Open释放行命中之利,ClosedCAP用列计数上限在延迟与能耗间找平衡。读懂 generic_ddr_controller.cpp 的 tick 流程与上述几个计数器,你就能为自己的工作负载挑出最合适的组合,并用controller_scheduling测试快速验证它确实按预期工作。
【免费下载链接】ramulator2
Ramulator 2.0 is a modern, modular, extensible, and fast cycle-accurate DRAM simulator. It provides support for agile implementation and evaluation of new memory system designs (e.g., new DRAM standards, emerging RowHammer mitigation techniques). Described in our IEEE CAL 2023 paper: https://arxiv.org/pdf/2308.11030.
相关推荐
Linux内核实时调度:如何选择最适合你的调度策略?🤔
Linux内核实时调度:如何选择最适合你的调度策略?🤔 在嵌入式开发、工业自动化等高实时性要求的场景中,你是否经常遇到任务响应延迟、系统卡顿的问题?Linux
文档教程操作系统知识库Xilem与Masonry深度对比:如何选择适合你的UI框架
Xilem与Masonry深度对比:如何选择适合你的UI框架 Xilem和Masonry是Rust生态中两个重要的UI框架,它们共同构成了一个完整的GUI解决方
前端桌面应用跨平台Rhino与Nashorn对比:如何选择适合的JavaScript引擎
Rhino与Nashorn对比:如何选择适合的JavaScript引擎 在Java生态系统中,选择正确的JavaScript引擎对于项目成功至关重要。Rhino
语言运行时编程语言解释器
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考