01-introduction
并行度
计算范式的区别
- 中心化计算:所有计算机资源集中在一个物理系统
- 并行计算:所有处理器要么与集中式共享内存紧密耦合,要么与分布式内存松散耦合
- 分布式计算:由多个自主的计算机组成
- 云计算:虚拟资源
关键问题
- 可扩展性 Scalability
- 效率 Efficiency
- 可靠性 Dependability
- 编程模型 Programming Models
- 耗电量 Power Consume
性能 Performance
- 大型程序的速度
- 多个小型程序的吞吐量
阿姆达尔定律
串行代码比例决定了最大加速比
摩尔定律
自从发明集成电路以来,集成电路上每平方英寸的晶体管数量每年增加一倍。
02a_ILP_pipeline
CPU性能方程
怎样减少CPU时间
增加CPU频率
- 多管线 Pipeline
增加IPC(每个周期的指令)
- 超标量 Superscalar
- 超长指令字 VLIW
减少程序中的指令
- 复杂指令与简单指令
MIPS数据路径的五个步骤
1.存储器取指令
2.指令译码同时读寄存器
3.执行操作或计算地址
4.在数据存储器中读取操作数
5.结果写回寄存器
流水线限制:冲突阻止下一条指令在其指定的时钟周期内执行
- 结构性冲突:无法支持此指令组合(比如只有一个人时,无法同时折叠和收起衣服)
- 数据冲突:指令取决于仍在管道中的先前指令的结果(比如同一双袜子只能先洗再收起来)
- 控制冲突:由获取指令和决定控制流变化(分支和跳转)之间的延迟引起。
Instr3中的取指令和Load中的写内存冲突,Instr3变为Stall
流水线中代码加速方程
举例:
三个通用数据冲突
读后写和写后写在MIPS的5步流水线中不会发生,因为读寄存器永远在第二步,而写寄存器永远在第五步
- 写后读 Read After Write(RAW)
- 读后写 Write After Read(WAR)
- 写后写 Write After Write(WAW)
转发
优化代码
条件控制冲突
流水线其他问题
异常:指令执行期间发生异常事件{由执行指令引起}
示例:除以零,未定义的操作码
中断:将处理器切换到新指令流的硬件信号{不是直接由代码引起的}
示例:声卡在需要更多音频输出样本时中断(如果音频在等待,则会发生“喀哒”声)
精确中断问题:尽管此时正在执行多条指令,但看起来好像2条指令(Ii和Ii + 1)之间出现了异常或中断
直到Ii为止的所有指令均已完全完成
保存Ii之后的任何指令均无效
精确中断后,中断(异常)处理程序要么中止程序,要么在指令Ii + 1处重新启动
o2b_Cache
内存分类
静态RAM(SRAM)
- 每位6个或8个晶体管
- 两个反相器(4个晶体管)+ 用于读取/写入的晶体管
- 针对速度(第一)和密度(第二)进行了优化
- 快速(亚纳秒级延迟,适用于小型SRAM)
- 速度大致与其面积成正比
- 与标准处理器逻辑完美融合
动态RAM(DRAM)
- 每位1个晶体管 + 1个电容器
- 针对密度进行了优化(就每位成本而言)
- 慢(内部访问> 40ns,引脚对引脚〜100ns)
- 不同的制造步骤(与逻辑不能很好地融合在一起)
增强DRAM性能的方法
快速页面模式
- 添加定时信号,该信号允许重复访问行缓冲区而无需另外的行访问时间
- 这样的缓冲区是自然而然的,因为每个数组为每次访问缓冲1024至2048位
同步DRAM(SDRAM)
- 将时钟信号添加到DRAM接口,以便重复的传输不会遭受与DRAM控制器同步的时间开销
双倍数据速率(DDR SDRAM)
- 在DRAM时钟信号的上升沿和下降沿都传输数据使峰值数据速率加倍
- DDR2通过将电压从2.5伏降至1.8伏来降低功耗+提供更高的时钟频率:高达400 MHz
- DDR3降至1.5伏+更高的时钟速率:高达800 MHz
提高的是带宽而不是延迟
内存优化
显存
- 达到DRAM或DDR3带宽的二到五倍(更宽的接口32bit vs. 16bit,更高的时钟频率)
减少SDRAM的功耗
- 降低电压
- 低功耗模式(忽略时钟,直到刷新)
局部性和缓存
数据局部性
- 时间性:如果现在需要数据项,则很可能在不久的将来再次需要它
- 空间性:如果现在需要数据项,则不久的将来可能需要附近的数据
利用位置:缓存
- 将最近使用的数据保存在处理器附近的快速存储器中
- 也将附近的数据带到那里
缓存基本原理
快速(但较小)的内存接近处理器
引用数据时
- 如果在缓存中,使用缓存而不是内存
- 如果不在缓存中,带入缓存(实际上,也要带入整个数据块)
- 也许必须踢出别的东西去做!
重要决定
- 放置:块可以放在缓存中的什么位置?
- 标识:我们如何在缓存中找到一个块?
- 替换:踢出什么来在缓存中腾出空间?
- 写策略:我们如何处理存储?
缓存由块大小的行组成
- 行大小通常为2的幂
- 通常大小为16到128个字节
例子
- 假设块大小为128个字节
- 最低的7位确定块内的偏移量
- 读取地址A = 0x7fffa3f4的数据
- 地址开始以基地址0x7fffa380阻塞
缓存放置
放置
- 允许哪些内存块进入哪些高速缓存行
放置策略(https://zhuanlan.zhihu.com/p/...
- 直接映射(块只能转到一行)
- 完全关联(块可以转到任何行)
- 集关联(块可以转到N行之一)
-- 例如,如果N = 4,则高速缓存为4路集关联
-- 其他两个策略是极端的(例如,如果N = 1,我们将获得直接映射的缓存)
一个四路组相连缓存实例问题
考虑这么一个问题,32 KB大小4路组相连cache,cache line大小是32 Bytes。请思考以下2个问题:
多少个组?
假设地址宽度是48 bits,index、offset以及tag分别占用几个bit?
总共4路,因此每路大小是8 KB。cache line size是32 Bytes,因此一共有256组(8 KB / 32 Bytes)。由于cache line size是32 Bytes,所以offset需要5位。一共256组,所以index需要8位,剩下的就是tag部分,占用35位。这个cache可以绘制下图表示。
缓存识别
当引用地址时,需要
- 查找其数据是否在缓存中
- 如果是,请在缓存中找到位置
- 这称为缓存查找
每条缓存行必须具有
- valid bit(如果行有数据,则为1;如果行为空,则为0)
- tag 用来识别缓存行里的
写策略
我们是否在写操作中分配缓存行?
Write-allocate
- 写未命中将块放入高速缓存
No-write-allocate
- 写未命中将缓存保留原样
我们是否在写入时更新内存?
Write-through
- 内存在每次写入时立即更新
Write-back
- 更换缓存行时内存更新
缓存性能
缓存缺失3C
Compulsory:必须有的
- 第一次访问每个块的Miss
Capacity:由于缓存容量有限
- 如果缓存大小无限,则不会有它们
Conflict:由于关联性有限
- 如果缓存是完全关联的,则不会有它们
减少缓存缺失率
更大的缓存
- 更少的Capacity Misses,但命中延迟更长
更高的关联性
- 更少的Conflict Misses,但命中延迟更长
Way预测
- 加速集合关联缓存
- 预测N路中的哪一路拥有我们的数据,作为直接映射的缓存快速访问
- 如果预测错误,再次作为集合关联缓存访问
伪关联缓存
- 类似于路途预测
- 从直接映射的缓存开始
- 如果错过“主要”条目,尝试其他条目
编译器优化
- 循环互换
- Blocking
减少命中时间
- 使用小而简单的缓存
- 使用虚拟内存地址索引缓存
- 隐藏缺失延迟(重叠缺失延迟、使用不阻塞缓存)
缓存prefetch
- Simple Sequential Prefetch
- Strided Prefetch
02c_BranchPred
三种分支预测
- 直接跳转,函数调用(永远执行,简单定位)
- 条件分支(预测困难,简单定位)
- 间接跳转,函数返回(永远执行,定位困难)
03a_ILP_superscalar_VLIW
单周期多指令
向量处理
- 超标量 Superscalar
- 超长指令字 VLIW
并行处理
- 双核CPU
03b_TLP
Thread Level Parallelism (TLP)
- ILP采用循环或直线代码段隐式地执行并行操作
- TLP采用多线程运行显式地执行并行操作
- 对于许多应用程序而言,使用TLP可能比使用ILP更具成本效益。
多线程策略
细粒度的多线程 Fine-Grained Muiltithreading
- 每个指令周期都切换线程
- 优点是可以跳过一切大的和小的停滞
- 缺点是它会减慢单个线程的执行速度,因为准备好执行而没有停顿的线程会被其他线程的指令延迟
粗粒度的多线程 Course-Grained Multithreading
- 当代价比较大的停顿产生时切换线程,例如L2缓存缺失
- 优点是线程切换很快,不减慢任何线程,因为只有停顿时才切换线程
- 缺点是由于管道启动成本的原因,很难克服因停顿时间短而造成的吞吐量损失
超线程 Simultaneous Multi-threading
- 想法是将单个大型单处理器用作多处理器
对于N路(N线程)SMT,我们需要:
- 能够从N个线程获取
- N套寄存器(包括PC)
- N个重命名表(RAT)
- N个虚拟内存空间
- 但是我们不需要复制整个OOO执行引擎(调度程序,执行单元,旁路网络,ROB等)。
03c_DLP
异构计算 Heterogeneous Computing
03d_Cache Coherence
缓存一致性定义
内存系统是连贯的,如果
- 如果在W和R之间没有其他处理器写入X,则从处理器P1上的地址X读取最近的写入W写入的值。
- 如果P1向X写入并且在足够的时间后P2读取X,并且在两次之间没有其他写入X,则P2的读取将返回P1的写入所写入的值。
- 对同一位置的序列化写入:所有处理器都以相同的顺序看到对位置X的两次写入。
- 属性一:保留程序顺序,在没有共享的情况下,每个处理器的行为就像一个单处理器
- 属性二:所有处理器最终都必须看到对一个地址的任何写操作,如果P1写入X并且P2继续读取X,则P2最终必须看到新值
属性三:保留因果关系
- 假设X从0开始。处理器P1使X递增,处理器P2等待直到X为1,然后将其递增为2。处理器P3必须最终看到X变为2。如果不同的处理器可以按不同的顺序看到写入,则P2可以看到P1的写入并执行自己的写入,而P3首先看到的是P2的写入,然后是P1的写入。 现在,我们有两个处理器将永远不同意A的值。
缓存一致性硬件方案
共享缓存
- 轻而易举地增强连贯性
- 不可扩展(L1缓存很快成为瓶颈)
窥探 Snooping
- 需要广播网络(如总线)来增强一致性
- 每个具有块的缓存均自行跟踪其共享状态
目录 Directory
- 即使使用点对点网络也可以增强一致性
- 一个区块只有一个保留其完全共享状态的地方
窥探 Snooping
- Write-update (with broadcast)
- Write-invalidate
三种状态
Invalid:B未缓存在C中
- 要读取或写入,必须在总线上发出请求
Modified:B在C中为脏数据
- C拥有该块,其他缓存没有该块,并且C替换B时必须更新内存
- 无需经过总线即可读写B
Shared:B在C中是干净的
- C具有该块,其他缓存也具有该块,并且C取代B时无需更新内存
- 不用经过总线便可以读B
- 要写入,必须向总线发送update请求
MSI协议
基于目录的缓存一直性
- 通常在分布式共享内存中
- 对于每个本地内存块,本地目录都有一个条目
目录条目指示
- 谁缓存了块的副本
- 他们处于什么状态
每个条目都有
- 一个脏位dirty bit(如果有脏缓存副本,则为1)
- 一个状态向量(每个节点1位)告知哪些节点可能已缓存副本
- 所有的miss都会发送到block的home node
- 目录执行所需的一致性动作
- 最终,目录响应数据
MESI状态转移图
https://blog.csdn.net/somehow...
04e_Interconnect
对于routing的网络延迟
- Switch delay:交换机中从incoming到outgoing link的时间
- Switches:沿路上switch的数量
Transfer time:Time to send the packet through a link。数据从发送到对方完全接受到所有数据的总时间,包括发送时间,存储转发时间,传播时间。
- vs 传播时间:数据在收,发两端的线路间传输所用得时间。
Store-and-Forward end-to-end transfer time: (不知道怎么算出来得)
- (SwitchesSwitchDelay)+(TransferTime(Switches+1))
Wormhole or Cut-Through end-to-end transfer time:
- (Switches*SwitchDelay) + TransferTime
switch技术
我们想要的switch是:有很多输入输出link,内部低冲突,短switch延迟
- Crossbar:很低的switching延迟,没有内部冲突,但随着link的上升复杂度上升
- Omega network:switch使用更小的crossbar有更多的port,每个link的复杂度小,但高延迟和高冲突
复杂度
Complexity | Distance | |
---|---|---|
Crossbar | O(N2) | 1 |
Omega | O(NlogN) | logN |
Cycle | O(N) | 1,N/2 |
2D-Mesh | O(N) | 1,2根N |
3D-Mesh | O(N) | 1,3根N |
06-并行程序设计引论
上图如果有m个处理器,加速比是多少
- m=1: T=2N-1, S=1
- m=N: T=logN, S=2N-1/T
- m=N/2: T=1+logN, S=2N-1/T
- m=N/4: T=4+logN, S=2N-1/T
- m=N/2^k: T=2^k+logN, S=2N-1/T
10b_MapReduce GFS Implementation
GFS
- 客户端向Master请求chunk每个副本所在的ChunkServer,其中ChunkServer持有修改租约。如果没有ChunkServer持有租约,那么Master会发起一个任务,将租约授权给一台ChunkServer
- Master返回客户端主副本和各副本所在的ChunkServer的位置信息,客户端将缓存这些信息供以后使用
- 客户端将要追加的记录发送到每一个副本。GFS中采用数据流和控制流分离的方法,从而能够基于网络拓扑结构很好地调度数据流的传输
- 当所有副本都确认收到了数据,客户端发起一个写请求控制命令给主副本
- 主副本把写请求提交给所有的备副本
- 各备副本成功完成后应答主副本
- 主副本应答客户端,如果有副本发生错误,将出现主副本写成功但某些备副本不成功的情况,客户端将重试
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。