我们已经了解整个SoC计算机系统如何执行程序了
本次课内容: 体系结构优化
性能 = 程序执行时间
给定一个程序, 性能优化的方向
inst/prog
- 编译优化, 更好的指令集设计cycle/inst
(CPI) - 增加IPC, 体系结构优化time/cycle
- 增加主频, 电路关键路径优化,
后端物理设计优化性能 = 程序执行时间
我们需要在仿真环境中做一些统计工作
inst/prog
- 程序的指令数
cycle/inst
- CPI, 不过一般统计IPC
time/cycle
- 每周期的时间
time
不是主机的真实时间, 是仿真环境中的时间我们需要知道程序的运行时间都花在哪里
我们关心的问题
科学的性能瓶颈定位方法:
/--- frontend ---\ /-------- backend --------\
+-----+ <--- 3. computation efficiency
+--> | FU | --+
+-----+ +-----+ | +-----+ | +-----+
| IFU | --> | IDU | --+ +--> | WBU |
+-----+ +-----+ | +-----+ | +-----+
^ +--> | LSU | --+
| +-----+
1. instruction supply ^
2. data supply --+
将处理器分为前端和后端, 如果希望处理器全速运行:
不过 “一生一芯”主要是教学, 所以要求也不用那么苛刻
access time /\ capacity price
/ \
~1ns / reg\ ~1KB $$$$$$
+------+
~10ns / DRAM \ ~10GB $$$$
+----------+
~10ms / disk \ ~1TB $$
+--------------+
~10s / tape \ >10TB $
+------------------+
“一生一芯”SoC目前采用SDRAM
用这个SDRAM, 如果你的CPU能跑500MHz
access time /\ capacity price
/ \
~1ns / reg\ ~1KB $$$$$$
+------+
cache -----> ~3ns / SRAM \ ~30KB $$$$$
+----------+
~10ns / DRAM \ ~10GB $$$$
+--------------+
~10ms / disk \ ~1TB $$
+------------------+
~10s / tape \ >10TB $
+----------------------+
关键思想: 加一层SRAM, 尽可能将程序需要访问的数据存在SRAM中
冷知识: cache发音同cash
问题是, 我们真的可以 “尽可能将程序需要访问的数据存在SRAM中”吗?
架构师发现, 程序对内存的访问存在若干规律
这些现象和程序的结构和行为有关
人类生活中也存在局部性原理
副本 = cache块 = 内存中的数据
设计cache需要解决如下问题
// 用之前的例子, 4路组相联, 有4096/4=1024组
31 14 13 4 3 0
+---------+-------+--------+
| tag | index | offset |
+---------+-------+--------+
CPU会发出写请求, cache如何处理?
cache的一般工作流程:
不同情况做不同的事情 = 状态机!
ASIC流程中需要使用工艺库提供的SRAM原语
SRAM原语的规格种类是有限的, 需要根据SRAM原语的规格来写RTL
此外, SRAM的读延迟(用ns衡量)决定了处理器主频的上限
在流水线/乱序执行CPU中, IFU和LSU通常同时工作, 都要访问cache
一般采用分离cache的方案, 即IFU访问icache, LSU访问dcache, 两者区别如下
将两者分开可以进行针对性的设计, 包括组织方式, 替换/预取算法等
此外, 若两者合并, 则需要采用真双口的SRAM, 同时处理两个访问请求
真实的CPU上一般配备多级cache, 若在其中一级缺失, 则访问下一级
一般来说, 不同级别的cache有不同的设计目标
cache每次访问内存时, 总是读写一个块的大小
方式1 - 拆分成多个总线事务请求
CPU一次通常只访问一个cache块中的一部分
cache缺失时, 可以先读出CPU需要的部分(critical word)
CPU: lw a0, 0x8
+------+
16 V8 4 0
+---+---+---+---+
|4th|3rd|2nd|1st|
+---+---+---+---+
1 30 1 1 1 1
|---|----------|---|---|---|---|
3rd 4th 1st 2nd
\ transaction /
RTFM: axburst的wrap模式
缓存有那么多参数, 那么多策略, 怎么选合适?
评估指标: 命中率, IPC, 主频, 面积, …
这些指标的表现都不错, 才是一个比较好的方案
快速的设计空间探索: 用较低的开销评估某些容易评估的指标
用C代码模拟cache的工作流程
我们可以把NEMU中的mtrace作为这个cache模拟器的输入!
体系结构顶会ISCA多次举行基于ChampSim模拟器的大赛
在模拟器中用C代码实现周期信息的统计
我们可以在周期精确的模拟器上统计IPC!
考虑主频/面积等因素, 用RTL实现这些方案
真实情况: 处理器体系结构的研究和探索并不是在RTL代码上进行的
有一个xxx想法
作业: 不问大佬, 自己探索cache每个参数具体如何影响程序性能
#include <stdint.h>
int main() {
const int BUSY = 0x0;
volatile uint8_t *status = (uint8_t *)(uintptr_t)0x400;
volatile uint8_t *data = (uint8_t *)(uintptr_t)0x404;
while (*status == BUSY); // wait until idle
*data = 0;
return 0;
}
0000000000000000 <main>:
0: 40004783 lbu a5,1024(zero) # 400 <main+0x400>
4: fe078ee3 beqz a5,0 <main>
8: 40000223 sb zero,1028(zero) # 404 <main+0x404>
c: 00000513 li a0,0
10: 00008067 ret
如果lbu指令的结果进入cache, 会发生什么?
回顾: 访问设备有副作用, 会改变其状态
如果icache和dcache包含同一个副本, 而且dcache对其更新, 则需要考虑两者的一致性问题
采用写通策略也不能100%解决
RISC-V提供fence.i指令, 让程序指示硬件进行代码和数据的同步
写dcache时 | icache缺失时 | 执行fence.i时 |
---|---|---|
更新icache中相应块 | - | nop |
无效icache中相应块 | 先检查dcache相应块 | nop |
- | 先检查dcache相应块 | 无效整个icache |
- | - | 无效整个icache,
写回dcache中的脏块 |
推荐最后一种, 容易实现
DMA也会访问内存, 需要考虑dcache和DMA访问的内存之间的一致性
因为cache控制指令容易造成安全问题, RISC-V将其作为可选扩展
多核CPU可能会访问同一个cache块
如果系统中有多个芯片共享内存, 还要考虑多个芯片之间的cache一致性