并行硬件和并行软件

本文介绍了Von Neumann结构及其导致的瓶颈,探讨了进程、多任务和线程的概念。针对Von Neumann模型,提出了缓存、虚拟内存和指令级并行等改进措施,详细阐述了CPU Cache的工作原理、虚拟内存的页表管理和硬件多线程技术,旨在提高计算机系统的性能和并行性。

From:《并行程序设计导论》

2.1.1 Von Neumann 结构

  经典的Von Neumann结构包括主存,CPU,以及主存与CPU之间的互连结构。

  Von Neumann瓶颈:主存与CPU之间的分离(互连结构)。

  瓶颈的原因:互连结构的分离限制了指令和数据访问的速度。

2.1.2 进程、多任务以及线程

  操作系统(Operating System )是一种用来管理硬件和软件资源的主要软件。

  进程是运行着的程序的一个实例。当用户运行一个程序时,操作系统创建一个进程。

  单核系统也可以多任务,提供对同时运行多个程序的支持。原因:每个进程只运行一个时间片(时间片是规定好的一小段时间,有几毫秒)。在一个程序执行一个时间片后,OS就切换执行其他程序。

  阻塞:某一个过程停止运行以等待所需资源。一个多任务操作系统中,如果一个进程需要等待某个资源,例如需要从外部的存储器中读取数据,它会阻塞。

  线程为程序员提供一种机制,将程序分为多个大致独立的任务。当某个任务阻塞时能执行其他任务。

  线程包含在进程中,所有线程可以使用相同的可执行代码,共享相同的内存和相同的I/O设备。实际上,当两个线程同属于一个进程时,他们共享进程的大多数资源,两者之间最大的差异是各自需要一个私有的程序计数器和函数调用栈,使它们能够独立运行。

2.2 对Von Neumann模型的改进

  介绍三种改进措施:缓存(caching)、虚拟内存、低层次并行。

2.2.1 Cache基础知识

  本书中谈到Cache,一般指的是CPU cache。

  CPU cache是一组相比于主存,CPU能更快速访问的内存区域。 CPU cache位于与CPU同一块的芯片或者位于其他芯片上,但比普通的内存芯片能更快地访问。

  cache里存放什么样的数据:程序接下来可能会用到的指令和数据与最近访问过的指令和数据在物理上是临近存放的。在执行完一条指令后,程序通常会执行下一条指令。同样,当程序访问一个内存区域后,同城会访问物理位置靠近的下一个区域。——局部性原理。

  改进1:为了利用局部性原理,系统使用更宽的互连结构来访问数据和指令。即:一次内存访问能存取一整快代码和数据。而不只是单条指令和单条数据。这些块称为高速缓存块或者高速缓存行。一个典型的高速缓存行能存储 8~16 倍单个内存区域的信息。比如一个高速缓存行可以存放16个浮点数。

  CPU cache不是单一结构。cache分为不同的层(level)。第一层(L1)最小但是最快,更高层(L2,L3...)更大但是相对较慢。cache通常是用来存储速度较慢的存储器中信息的副本,可以认为低层是高层cache的cache。所以,一个变量存储在L1 cache中,也会存储在L2 cache中。但是,有些多层cache不会复制已经在其他层cache中存在的信息。对于这种cache, L1 cache中的变量不会存储在其他层cache中,但会存储在主存中。

    结构示意图:

                                                     main memory

                                                                |

                                                         L3 cache

                                                                |

                                                         L2 cache

                                                                |

                                                         L1 cache

                                                                |

                                                            CPU

  越靠近CPU的cache越快

  当CPU需要访问指令或者数据时,查询次序是由近及远找。当向cache查询信息时,如果cache中有信息,则称为cache命中(hit)。如果信息不存在则称为cache 缺失(miss)。

  当CPU尝试读取数据或者指令时,如果发生cache缺失,那么就会从主存中读出包含所需信息的整个高速缓存块。这是CPU会阻塞,以等待速度想读较慢的主存:主力其可以停止执行当前程序指令,知道从主存中取出所需要的数据或者指令。

  当CPU尝试向cache中写数据时,cache中的值与主存中的值就会不同或者不一致(inconsistent)。

  不一致问题的两种解决方案。写直达(write-through):当CPU向cache写数据时,高速缓存行会立即写入主存中。——cache写一个,主存写一行。

                                                      写回(write-back):数据不是立即更新到主存中,而是将发生数据更新的高速缓存行标记成脏(dirty)。当发生高速缓存行替换时,标记为dirty的高速缓存行被写入主存中。


2.2.2 Cache 映射

  在Cache设计中, 另一个问题是高速缓存行应该存储在什么位置。 当从主存中取出一个高速缓存行时,应该把这个高速缓存行放到Cache中的什么位置?——系统不同位置不同。

  一种放置方式是全相连(fully associative)cache, 每个高速缓存行能够放置在cache中的任意位置。

  一种放置方式是直接映射(directed mapped)cache,每个高速缓存行在cache中有唯一的位置。

  还有一种方式是n路组相联(n-way set associated)cache,每个高速缓存行都能放置到cache中n个不同区域位置中的一个。n=2时,假设cache有8行,可以将cache分为2组,0~3行1组,4~7行1组,每个高速缓存行可以映射到2组中某一组(分到哪个组可以用取模来得到)任意位置。

  当内存中的行(多于1行)能被映射到cache中的多个不同位置(全相联和n-路组相联)时,需要决定替换或驱逐cache中的哪一行。例如,主存中的第4行可以替换的位置是cache中的0,1行,但此时都被其他内存行占据着,采用 least recently used (最近最少使用)方案。即 cache 记录各个块被访问的次数,替换最近访问次数最少的块。在刚刚的例子中,主存4号行替换 cache 0,1行中最近访问次数少的那一行。

2.2.4 虚拟存储器

  多任务OS中,当程序需要访问大型数据集时,指令或数据可能在主存中放不下。

  利用虚拟存储器(虚拟内存),使得主存可以作为辅存 (即外存,硬盘,U盘,光盘,软盘等外部存储) 的缓存。它通过在主存中存放当前执行程序所需要用到的部分,来利用时间和空间局部性;那些暂时永不到的部分存储在辅存的块中,称为交换空间(swap space)。

  虚拟存储器也是对数据块和指令块进行操作(这与CPU cache 类似),这些块通常称为页(page)。因为访问辅存比访问内存慢几十万倍,所以页通常比较大。大多数系统采用固定大小的页,从4~16kB不等。

  如果在编译程序时直接给页指定物理内存地址,那么就会陷入麻烦。因为这样中的每一页也都必须指定一块内存来存放,在多任务操作系统中,(由于放不过来)就会导致多个程序要使用相同的内存块(这样发生页替换时时间耗费非常大)。为了避免这个问题,在编译程序时,给程序的页赋予虚拟页号。当程序运行时,创建一张将虚拟页号映射成物理地址的表。程序运行时使用到虚拟地址,这个页表就用来将虚拟地址转换成物理地址。加入页表的创建由操作系统管理,那么就能保证程序使用的内存不会与其他程序使用的内存重叠(页数还是那么多,内存就是那么打,怎么保证?

  使用页表的缺点是,会使访问主存区域的时间加倍。假设,想要执行主存中的一条指令,执行程序只有该指令的虚拟地址,在从内存中找到该指令之前,需要将虚拟地址转换成虚拟地址,为了能够转换成虚拟地址,需要在内存中寻找包含该指令的页。现在,虚拟页号作为虚拟地址的一部分存储。假如虚拟地址有32位,页大小是4KB = 4096字节,那么可以用12位来标识页中的每个字节。这是因为2^12 = 4096。因此,可以用虚拟地址的低12为来定位页内字节,而剩下的位用来定位页。

                             直接从虚拟地址中计算出来                    访问页表                                    

  程序的虚拟地址————————————>虚拟页号—————————>物理页号       

                                              不访存                                    将虚拟页号转换得到                  
  然后根据物理页号访问cache,若所需页表不在则从内存加载到cache中,加载结束后,就能将虚拟地址转换成物理地址并获得需要的指令

 

  问题又来了,尽管多个程序可以同时使用主存了,但使用页表会增加程序总体的运行时间 。为了解决这个问题,处理器有一种专门用于地址转换的缓存,叫转译后备缓冲区(Translation-Lookaside Buffer, TLB)。TLB 在快速存储介质中缓存了一些页表的条目(通常为16-512条)。利用时间和空间局部性原理,大部分存储器所访问页的物理地址已经存储在TLB 中,对主存中页表的访问能够大幅度减少。

  TLB的术语和Cache的术语一样。当查询的地址和虚拟页号在TLB中时称为TLB命中; 如果不再TLB中,称为TLB缺失。但是,有些术语也不同,加入想要访问的页不在内存中,即页表中该页没有合法的物理地址,该页只存储在磁盘上,那么这次访问称为页面失效(page fault)。

  磁盘访问的相对迟缓也会给虚拟内存带来一些额外的后果。第一,在CPU cache中,可以使用写直达或者写回方案处理缺失问题,但在虚拟内存中,磁盘访问的代价太大,需要进尽可能地避免,所以虚拟内存通常采用写回方案。可以在内存中为每个页设置一位,标识该页是否被更新过。如果该页被更新过,则在页从内存中替换出去时,需要把它写入磁盘。第二,因为磁盘访问迟缓,管理页表和处理磁盘访问由操作系统来完成。因此,程序员不能直接控制虚拟内存。CPU Cache 是由系统硬件控制,而虚拟内存是由系统硬件和操作系统一起控制的。


2.2.5 指令级并行,ILP

  指令级并行(Instruction-Level parallelism,ILP)通过让多个处理器不见或者功能单元同时执行指令来提高处理器的性能。有两种方法来实现指令级并行:流水线和多发射。流水线是指将功能单元分阶段安排;多发射是指让多条指令同时启动。

1.流水线

  流水线通过将功能分成多个单独的硬件或者功能单元,并把它们按顺序串接来提高性能。

2.多发射

  多发射处理器通过赋值功能单元来同时执行程序中的不同指令。它尝试将不同指令集应用于不同数据项。

  为了能够利用多发射,系统必须找出能够同时执行的指令。其中一种最重要的技术是预测(speculation)。在预测技术中,编译器或者处理器对一条指令进行猜测,然后在猜测的基础上执行代码。

  预测执行允许预测错误的情况发生。如果预测工作由编译器来做,那么它通常在代码中嵌入测试语句来验证预测的正确性。如果预测错误就会执行修正操作。加入由硬件做预测操作,处理器一般会将预测执行的结果缓存在一个缓冲器中。如果预测正确,缓冲器中的内容会传递给寄存器或者内存;如果预测错误,则缓冲器中的内容被丢弃、指令重新执行。

  尽管动态多发射系统能够乱序执行指令,但在现行的系统中,指令是顺序加载的,执行的结果也是顺序提交的。即指令的结果是按程序中规定的顺序写入寄存器和内存中的。

2.2.6 硬件多线程

  我们很难利用指令级并行,因为程序中有许多部分之间存在依赖关系。例如直接计算斐波那契数:

f[0] = f[1] = 1;
for (i = 2; i <= n; i++)
 f[i] = f[i-1] + f[i-2];

  实质上根本没有可以同时执行的指令。

 线程级并行TLP(Thread-Level Parallelism)尝试通过同时执行不同线程来提供并行性。与ILP相比,TLP提供的是粗粒度的并行性,即同时执行的程序基本单元(线程)比细粒度的程序单元(单条指令)更大或者更粗。

 硬件多线程(hardware multithreading)为系统提供了一种机制,使得当前执行的任务被阻塞时,系统能够继续其他有用的工作。例如,如果当前任务需要等待数据从内存中读出,那么它可以通过执行其他线程而不是继续当前线程来发掘并行性。当然,为了使这种机制有效,系统必须支持线程间的快速切换。例如,在一些较老的系统中,一个线程被简单地实现为一个进程,然而进程的切换时间是执行指令时间的数千倍。

 在细粒度(fine-grained)多线程中,处理器在每条指令执行完后切换线程,从而跳过被阻塞的线程。尽管这种方法能够避免因为阻塞而导致机器时间的浪费,但它的缺点是,执行很长一段指令的线程在执行每条指令的时候都需要等待。粗粒度(coarse-grained)多线程为了避免这个问题,只切换那些需要等待较长时间才能完成操作(如从主存中加载)而被阻塞的线程。这种机制的优点是,不需要线程间的立即切换。但是,处理器还是可能在短阻塞时候空闲,线程间的切换也还是会导致延迟。

 同步多线程(Simultaneous Multithreading,SMT )是细粒度多线程的变种。它通过允许多个线程同时使用多个功能单元来利用超标量处理器的性能。如果我们指定“优先”线程,那么能够在一定程度上减轻线程减速的问题。 优先线程是指有多条指令就绪的线程。







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值