一、多线程:硬件基础
一、多线程:硬件基础
1.高速缓存
处理器的处理能力要远远高于主内存DRAM的读写能力。进行一次主内存的读写所需要的时间,处理器可能足够处理上百条指令。为了弥补处理器与主内存巨大的效率差距,处理器的设计者们引入了高速缓存Cache。
1.1缓存结构
高速缓存的容量远小于主内存,但是读取速率要远远高于主内存。有了高速缓存后,处理器如果想要读取数据就可以直接从高速缓存中读取,而不用从主内存中读取。高速缓存中以一个类似于拉链散列表的形式存储主内存中的内容副本,其key是内存地址,value是该内存地址所存储的值,或者将要写入内存的的值。其结构包含若干桶,每个桶又包含若干缓存条目。
1.1.1缓存条目
每一个缓存条目又被划分成Tag、Data Block、Flag三个部分
Data Block又被称为缓存行是与内存进行数据交换的最小单元,其中存储着从内存中读取的数据或者将要写入内存中的数据。一个缓存行中可能存储多个变量的数据。
Tag包含了与缓存行中数据相对应的内存地址的部分信息。
Flag表示缓存行的状态信息。
1.1.2缓存命中与缓存未命中
处理器执行内存访问操作时会将对应的内存地址解码,其结果包含index、tag、offset。index用于定位桶,tag通过与缓存条目的tag对比后定位相应的缓存条目,offset则是该变量所在缓存行中的偏移量也就是储存该变量的起始位置。如果通过如上规则能够在高速缓存中找到对应的缓存行并且该缓存行所在的缓存条目的Flag值表示是有效的,则称这次内存访问操作产生的缓存命中,否则称这次内存访问操作产生了缓存未命中。
缓存未命中也分为读未命中和写未命中。如果发生了读未命中则处理器会从主内存中读取数据并存入相应的缓存行之中,这个过程会造成处理器停顿而不能执行其他指令影响处理器的性能。因此我们希望能够尽可能减少缓存未命中,但是由于缓存的容量远小于内存的容量,所有缓存未命中是不可避免的。
1.2缓存一致性协议
多线程共同操作同一个变量的情况下,执行这些线程的处理器都会在其缓存中保存该变量的副本。如果其中一个线程更新了该变量在缓存中的副本,那么其他线程是如果察觉并同步的呢?如果其他线程并未同步变量的更新,并且读取该变量的话就会得到老的数据。为解决这种情况,引入了以中通信机制:缓存一致性协议。
MESI协议将缓存条目的状态划分为:Modified、Exclusive、Shared、Invalid四种。并定义了一组消息用于协调各个处理器的读写内存操作,如下表所示。处理器要执行读写操作时会向总线Bus中发送特定的请求消息,其他处理器嗅探(Snoop也称拦截)总线中由其他处理器所发出的请求消息,并在一定条件下向总线中回复相应的响应消息。
| 消息名 | 消息类型 | 描述 |
|---|---|---|
| Read | 请求 | 通知其他处理器,主内存当前处理器正准备读取某个数据。该消息包含待读取数据的内存地址 |
| Read Response | 相应 | 该消息相应Read消息,其中包含被请求读取的数据,有可能是来自与其他处理器中的高速缓存提供的,也可能是主内存提供的 |
| Invalidate | 请求 | 通知其他处理器将其高速缓存中指定内存地址对应的缓存条目的状态Flag值置为I,即删除指定内存地址的副本数据(逻辑删除) |
| Invalidate Acknowledge | 响应 | 接受到Invalidate消息的处理器必须回复该消息,表示删除了其高速缓存上相应的副本数据 |
| Read Invalidate | 请求 | 该消息是Read消息和Invalidate消息的组合。作用在通知其他处理器当前处理器准备更新一个数据(Read-Modify-Write)接受到该消息的处理器必须回复Read Response消息和Invalidate消息 |
| Writeback | 请求 | 该消息包含需要写入主内存的数据及其对应的内存地址 |
举例:P1,P2两个处理器共享数据S,数据在储存在内存地址A上。
- 读内存操作流程图:
- 写内存操作流程图:
1.3写缓冲队列与无效化队列
如果你仔细思考写内存操作的过程便会发现,缓存一致性协议虽然保证了每个处理器上关于同一个共享变量的缓存一致性。但是为了保证缓存一致性付出了性能下降的代价,因为当处理器执行写操作时候必须等待其他所有处理删除他们缓存上相应的缓存条目,并且接受到其他所有处理器回复的Invalidate Acknowledge或Read Response后才能将数据写入高速缓存。为了减少这种由于等他其他处理器回复消息所造成的延迟,硬件的设计者们引入了写缓冲器和无效化队列。
引入写缓冲器后写内存操作的流程是:如果相应的缓存条目的状态是E或者M,则直接写入缓存而无序发送任何消息;如果相应缓存条目的状态是S,则先将要写的数据与对应的内存地址存入写缓冲器中的条目之中,并发送Invalidate消息;如果相应缓存条目的状态是I(称为写未命中,开销比较大,因为Read消息可能会造成读内存操作),则先将要写的数据与对应的内存地址存入写缓冲器中的条目之中,并发送Read Invalidate消息。当处理器将相应数据写入写缓冲器之后,对于执行写的处理器来说便认为写操作已经完成,因此不必等待Invalidate Acknowledge或Read Response消息可以继续执行其他指令。当执行写操作的处理器接受到其他所有处理器针对同一个缓存条目的Invalidate Acknowledge或Read Response消息时,该处理器会把针对相应内存地址的写操作结果写入相应的缓存行中,此时对于写操作以外的其他处理器来说写操作才算完成(因为此时其他处理器可以通过缓存一致性协议读取到这次写操作后的最新值)。
引入无效化队列后,处理器在接受到Invalidate并不删除相应地址的缓存条目,而是将消息存入无效化队列后就回复Invalidate Acknowledge消息,从而减少执行写操作处理器等待的时间。有些处理器可能没有无效化队列(X86)
1.3.1储存转发
引入写缓冲器之后,执行写操作并且最新的数据还存储的写缓冲器而没有写到相应的缓存行,如果此时执行该处理器执行读操作并直接读取缓存行中的数据的话便会读取到一个旧的值。因此,有了写缓冲器,处理器在执行读操作时会先查询写缓冲器,如果写缓冲器存在相应的条目则直接把值返回,如果没有再去高速缓存中读取。这种直接从写缓冲器中读取数据来实现内存读操作的技术被称为储存转发Store Forwarding(可以理解为本应存储在内存上的数据被转发到了写缓冲器上)

浙公网安备 33010602011771号