前言
选择题
- 对于绝对的选项多加小心
- 选不出来看看换个思路,能不能找出选项中的坑
汇编
- 填空时注意运算顺序,该加括号时加括号
- 填写机器码时,注意小端法
- 注意指令后缀,还有寄存器的使用长度!
- 填栈帧时,注意内存的刻度
处理器
- 处理器预热周期
- 循环间有潜在数据冒险
- 计算周期数时,单独考虑第一个/最后一个循环
- 注意到底是Cnd还是!Cnd
优化
- 注意考虑内存别名导致关键路径的情况
- 数据依赖图
缓存
-
注意顺序,默认是先进的在前面
-
认真读题,细心!
-
找充分大的地方,整齐的列出所有数据,先换算为16进制,再换算为2进制
Chp2 数据
1 整数
正数溢出为负数,负数溢出为正数
assert(0x80000000 > 0); // 十六进制数先转unsigned, 再转long; 这里转成unsigned
int x = 0x80000000;
assert(x < 0);
经典例子:INT_MIN == -INT_MIN
2 浮点数
空间分配
sign | exp | normalized range | frac | |
---|---|---|---|---|
float | 1 | 8 | -126~127 | 23 |
double | 1 | 11 | -1022~1023 | 52 |
特殊值
-
Nan
float nan1 = u2f(0xffc00000u); float nan2 = u2f(0xffc00001u); printf("%d %d\n", nan1==nan1, nan1!=nan1); printf("%d %d\n", nan1==nan2, nan1!=nan2);
结果:
0 1 0 1
简单地说就是:Nan != Nan恒成立
-
denormed
-
INF
3 float与int
- float转int,直接截取
- int/double转float,舍入
4 关于TMIN
cout << (-2147483648 > 0) << endl; // 0
cout << (0x80000000 > 0) << endl; // 1
解释:
十六进制转换顺序int->unsigned->long;十进制转换顺序:int->long
所以第一个被当成long对待,第二个被当成unsigned对待
5 大端法与小端法
大端法:高位放在前面(低地址)
小端法:低位放在前面(高地址)
6 其他
注意运算符优先级
类型转换时,先改变大小,然后进行有符号/无符号转换
Chp3 汇编
1 基础
格式 | 数值 |
---|---|
$Imm | Imm |
Imm | M[Imm] |
(r1,r2) | M[R[r1]+R[r2]] |
Imm(r1,r2) | |
(,r1,s) | M[s*R[r1]] |
Imm(,r1,s) |
scaler必须是1/2/4/8
寄存器
%r12~%r15和==%rbx、%rbp==一样,都是callee saved
%r8、%r9分别是第5、6个参数
后缀
qlwb
lea只能搭配q
操作数
一元算数/逻辑运算的操作数可以是内存
二元算术/逻辑运算的两个操作数都可以是内存,第一个操作数还可以是立即数
MOVS/Z类、CMOV类指令的第一个操作数可以是内存,第二个操作数只能是寄存器
CMOV类指令的操作数不能是单字节,通过寄存器后缀指明长度(movb、movl的后缀含义!)
条件码
指令 | 条件码 |
---|---|
leaq | 不设条件码 |
inc/dec | 设ZF和OF,不设CF |
逻辑运算 | 设CF和OF为0 |
移位运算 | 设CF,设OF为0 |
指令 | 条件 | 备注 |
---|---|---|
setl | OF^SF | |
setb | CF | |
sets | SF | 如果是负数 |
2 罕见汇编指令
数据传送相关
指令 | 效果 | 备注 |
---|---|---|
cltq | 把%eax符号扩展到%rax | convert long to quad |
movabsq | movabsq的操作数是立即数,可以是64位,目的只能是寄存器 | 而普通的movq只能是32位 |
算术相关
移位操作:k可以存放在==%cl==(单字节)中,按低m位取
sar:只有一个操作数时,k为1
指令 | 效果 | 备注 |
---|---|---|
(i)mulq S | rdx : rax ← \leftarrow ← S*rax | i表示有符号数 否则是无符号数 |
cqto | rdx : rax ← \leftarrow ← rax | consert quad to oct 用idivq前先cqto 用divq前先把rdx清零 |
(i)divq | rax
←
\leftarrow
← rdx:rax / S rdx ← \leftarrow ← rdx:rax % S | 注意S是除数 |
跳转相关
jmp *%rax
jmp *(%rax)
过程相关
leave
相当于:
movq %rbp, %rsp
popq %rbp
随后紧接着ret
3 逻辑
见复习材料P6~7
注意switch:jmp *JUMP_LIST(, index, 8)
,里面的==*==重要!
4 过程
栈帧布局
- (rbp)
- 寄存器(callee saved)
- 局部变量(不必8字节对齐,与struct对齐方式一致)
- 7+th参数(一律8字节对齐,%rsp+8*(k-6))
- 返回地址RA
call、jmp一般都用相对编码
5 浮点数
%xmm0 返回值
%xmm0~7 8个参数
Chp4 Y86处理器
条件码没有CF
寄存器没有%r15
call只能绝对寻址,不能PC相对寻址
push/pop %rsp的行为:总是处理原始值
逻辑门
圆的是AND,尖的是OR(正好和字母形状反过来)
CISC vs RISC
指标 | CISC | RISC |
---|---|---|
延迟 | 长短不一 | 都短 |
编码长度 | 不定长 | 定长(4字节) |
内存寻址 | 多样 | 只有基址和偏移量寻址 |
内存访问 | 算数/逻辑运算可访问内存 | load/store体系结构 |
算数/逻辑运算操作数 | 可以是内存 | 只能是寄存器 |
抽象程度 | 抽象 | 细节可见 |
条件码 | 有 | 无 |
过程 | 栈密集 | 寄存器密集 |
举例 |
概念
延迟:从头到尾处理一条指令所用的时间
吞吐量:单位时间处理的总指令数(单位:GIPS,或指令数/ns)
吞
吐
量
=
1
最
大
模
块
延
时
+
寄
存
器
延
时
(
p
s
)
∗
1000
吞吐量 = \dfrac{1}{最大模块延时+寄存器延时(ps)} * 1000
吞吐量=最大模块延时+寄存器延时(ps)1∗1000
其他
- 注意运行前要填充流水线,5阶段流水线要填充4个周期
- 注意循环可能导致潜在的数据冒险
- 计算周期数时,注意单独考虑第一次/最后一次循环
Chp5 优化
循环展开级数并不是越多越好,考虑容量不命中(寄存器也算)
Chp6 缓存
RAM
晶体管数/bit | 访问时间 | 成本 | 应用 | 敏感 | |
---|---|---|---|---|---|
SRAM | 6 | x1 | x1000 | 缓存 | 否 |
DRSM | 1 | x10 | x1 | 内存 | 是 |
传统DRAM
超单元:由 ω \omega ω个单元组成
DRAM芯片有rc=d个超单元
访问DRAM内容时,先发RAS请求,DRAM取出相应行的数据,放进一个缓冲区,再发CAS请求,复制出相应的 ω \omega ω位数据。RAS和CAS占用相同的引脚。两次发送是为了降低芯片的引脚数量
总共需要 ω + m a x ( l o g 2 r + l o g 2 c ) \omega+max(log_2r+log_2c) ω+max(log2r+log2c)个引脚
增强DRAM
名称 | 特点 |
---|---|
FPM | 对于同一行数据的访问,可以直接从缓冲区中读取,只发一次RAS请求即可 |
EDO | FPM的增强 |
SDRAM | 比异步的更快 |
DDR SDRAM | 相比SDRAM速度翻倍 |
VDRAM | 对图形系统的优化 |
ROM
擦写次数 | 应用 | |
---|---|---|
PROM | 1 | |
EPROM | 1000 | |
EEPROM | 10^5 | 闪存、SSD |
固件:ROM上的程序,例如BIOS、驱动程序
BUS
总线事务:读事务、写事务
总线:系统总线、内存总线
DISK
注意单位GB与GiB的区别
盘面->表面->磁道->扇区
柱面,个数等于每个表面的磁道个数
计算磁盘容量:注意每个盘片有两个表面
计算访问时间: = T a v g s e e k + 60 ∗ 1000 R P M ∗ ( 1 2 + 1 磁 道 平 均 扇 区 数 ) =T_{avg\ seek} + \dfrac{60*1000}{RPM}*(\dfrac{1}{2}+\dfrac{1}{磁道平均扇区数}) =Tavg seek+RPM60∗1000∗(21+磁道平均扇区数1)
寻道时间和旋转延迟大致相等,所以可以用寻道时间*2估计旋转延迟
磁盘控制器将物理磁盘与逻辑磁盘之间建立映射
概念:内存映射I/O
概念:DMA直接内存访问
SSD
读比写快
以页为单位读写
一页被擦除后才能写入数据
写慢的原因
- 擦除慢,1ms量级(读是50us量级)
- 若块中已有数据,要先复制
Cache
一路(way)有很多行(line)
缓存不命中的几种特殊情况
- 冷不命中/强制性不命中
- 冲突不命中
- 容量不命中
计算Cache Size
c a c h e s i z e = d a t a s i z e + ( v a l i d b i t s i z e + t a g s i z e ) ∗ b l o c k n u m b e r cachesize = datasize + (validbitsize + tagsize) * blocknumber cachesize=datasize+(validbitsize+tagsize)∗blocknumber
Cache参数的影响
命中时间 | 命中率 | 不命中处罚 | 有效数据占比 | |
---|---|---|---|---|
缓存大 | 增大(理解) | 增大(减少容量不命中) | —— | —— |
块大 | —— | 块大,空间局部性提高; 行数变小,时间局部性降低 | 增大(复制成本) | 增大 |
组相连度高 | 增大(理解) | 减少冲突不命中/抖动 可能放大容量不命中的影响 | 增大(选择牺牲行的成本) | 减小(tag位变长) |
存储结构越往下走,就越不能忍受不命中,宁可牺牲一点命中时间,因此会选择更高的组相连度
写策略影响
- 直写 & 非写分配
- 减少总线流量,增大复杂性(修改位dirty bit)
- 写回 & 写分配
- 层次较低的多用,因为不能忍受反复不命中