《数字系统架构》总结

最后修改日期:2023/10/25


书本:计算机组成与设计/Computer Organization and Design(Patterson&Hennessy)(RISC-V Edition)
按照上课顺序,但有跳过。


1. 计算机抽象及相关技术

1.1. 名词

1.1.1. Execution Time

Execution time (of a task) is the best way to measure the performance as system response time for a certain application.

Example: GPU training time.

1.1.2. IPC

Instructions Per Cycle.

1.1.3. MIPS

Million Instructions Per Second.

\[MIPS = IPC\times CPU Clock Frequency \]

1.1.4. Throughput

Throughput is the amount of work that can be performed or the amount of output that be produced by a system or component in a given period of time.

1.1.5. FLOPS

Instruction rate for floating point.

1.1.6. Energy Efficiency

Example: GFLOPS/W.

1.1.7. Benchmark

A benchmark is the act of running a computer program, a set of programs, or other operations, in order to assess the relative performance of an object, normally by running a number of standard tests and trials against it. The term benchmark is also commonly utilized for the purposes of elaborately designed benchmarking programs themselves.

1.2. 计算机架构中的思想

1.2.1. 面向摩尔定律的设计

提高处理器速度,可以通过提高主频:

  • 提升工艺
  • 加深流水线

1.2.2. 使用抽象化设计

将设计分成多个层次:

  • Transistors (物理机制)
  • Circuits(电路实现)
  • Logic (AND, OR, Add, Multiply…)
  • Modules (ALU, Decoder, Load/Store …)
  • Processor/Memory/IO(多核,向量,分布式 …)
  • Assembler/Complier (指令,数据 …)
  • OS (内存管理,任务调度,文件存储 …)
  • Applications (任务,人机交互 …)

1.2.3. 加速经常性事件

Amdahl定律:系统中某一部件由于采用更快的执行方式,整个系统性能的
提高与这种执行方式的使用频率或占总执行时间的比例有关。
可改进部分的比例为\(Fe\)改进部分的加速比\(Se\),则改进后的时间为:

\[T_n=T_0\left(1-Fe+\frac{Fe}{Se}\right) \]

改进后加速比为:

\[S_n=T_0/T_n=\frac1{1-Fe+\frac{Fe}{Se}} \]

1.2.4. 通过并行提高性能

  • 系统级并行

    • 分布式系统
  • 任务级并行

    • 多核,承担不同任务
  • 指令级并行

1.2.5. 通过流水线提高性能

此处略。

1.2.6. 通过预测提高性能

  • 分支预测

1.2.7. 存储层次

  • Register
  • Cache
  • Memory
  • Disk
  • Tape

1.2.8. 通过冗余提高可靠性

  • 通过更多器件的代价来提高可靠性

1.3. 计算机结构

1.3.1. Von Neumann结构

输入设备、控制器、ALU、存储器、输出设备。

1.3.2. 总线结构

单总线、三总线。

2. 指令

2.1. 数据类型

  • 标量(按IEEE 754标准)

    • int
    • double: 64 bits
    • float: 32 bits
    • half: 16 bits
  • 向量

  • 立即数

2.2. 编址

2.2.1. 常用编址单位

字编址、字节编址、位编址、块编址。一般机器是字节编址、字访问。

2.2.2. 大小端

image

2.2.3. 寻址方式

  • 立即数
  • 面向寄存器
  • 面向主存储器
  • 面向堆栈

2.3. 指令

2.3.1. 指令组成

  • 操作码OPC

    • 操作种类
    • 操作数描述
  • 地址码A

2.3.2. 指令系统

通用计算机有5类基本指令

  1. 数据传送类指令
  2. 运算类指令
  3. 程序控制指令
  4. 输入输出指令
  5. 处理机控制和调试指令

2.3.2.1. 性能

完整性、规整性、高效率和兼容性。

2.3.2.2. 分类

CISC、RISC、VLIW。

2.3.2.3. 二八定律

在CISC中,大约20%的指令占据了80%的处理机执行时间。

2.3.2.4. 同步指令

解决多个处理器或多个进程同步工作的问题。

  • 原子交换

    Compare and Swap (CAS) Compare a register value to a value in memory addressed by another register, and if they are equal, then swap a third register value with the one in memory.

3. RISC-V

3.1. R(Register)

Name Syntax Description Meaning
add add rd, rs1, rs2 \(x[rd] = x[rs1] + x[rs2]\) add
sub sub rd, rs1, rs2 \(x[rd] = x[rs1] − x[rs2]\) subtract
sll/sllw sll rd, rs1, rs2 \(x[rd] = x[rs1] \ll x[rs2]\) shift left logical
slt slt rd, rs1, rs2 \(x[rd] = (x[rs1] <_s x[rs2])\) set if less than
sltu sltu rd, rs1, rs2 \(x[rd] = (x[rs1] <_u x[rs2])\) set if less than, unsigned

3.2. I(Immediate)

Name Syntax Description Meaning
ld ld rd, imm(rs1) \(x[rd] = M[x[rs1] + sext(imm)][63:0]\) load doubleword
addi addi rd, rs1, imm \(x[rd] = x[rs1] + sext(imm)\) add immediate
lui lui rd, imm \(x[rd] = sext(imm[31:12] \ll 12)\) load upper immediate

3.3. S(Store)

Name Syntax Description Meaning
sd sd rs2, imm(rs1) \(M[x[rs1] + sext(imm) = x[rs2][63: 0]\) save doubleword

3.4. SB(Select Branch)

Name Syntax Description Meaning
beq beq rs1, rs2, imm \(if (rs1 == rs2),\enspace pc += sext(imm)\) branch if equal
bne bne rs1, rs2, imm \(if (rs1 \neq rs2),\enspace pc += sext(imm)\) branch if not equal

因为U和UJ类型没有用过,就不列举了。

4. 流水线

4.1. 五级流水线

4.1.1. 五级流水线的结构图

image

4.1.2. 五级流水线的简化结构图

image

4.1.3. 五级流水线的划分

  1. 取指令(IF)

    以程序计数器PC中的内容作为地址,从存储器中取出指令并放入指令寄存器IR;

    同时PC值加4(假设每条指令占4个字节),指向顺序的下一条指令。

  2. 指令译码/读寄存器(ID)

    对指令进行译码,并用IR中的寄存器地址去访问通用寄存器组,读出所需的操作数。

  3. 执行/有效地址计算(EX)

    load和store指令:ALU把指令中所指定的寄存器的内容与偏移量相加,形成访存有效地址。

    寄存器-寄存器ALU指令:ALU按照操作码指定的操作对从通用寄存器组中读出的数据进行运算。

    寄存器-立即数ALU指令:ALU按照操作码指定的操作对从通用寄存器组中读出的操作数和指令中给出的立即数进行运算。

    分支指令:ALU把指令中给出的偏移量与PC值相加,形成转移目标的地址。同时,对在前一个周期读出的操作数进行判断,确定分支是否成功。

  4. 存储器访问/分支完成(MEM)

    该周期处理的指令只有load、store和分支指令。其它类型的指令在此周期不做任何操作。

    • load指令:用上一个周期计算出的有效地址从存储器读出相应的数据;

    • store指令:把指定的数据写入这个有效地址所指出的存储器单元。

    • 分支指令: 分支“成功”,就把转移目标地址送入PC。

  5. 写回(WB)

    ALU运算指令和load指令在这个周期把结果数据写入通用寄存器组。其它类型的指令在此周期不做任何操作。

    • ALU运算指令:结果数据来自ALU。

    • load指令: 结果数据来自存储器。

4.2. 流水线的时钟周期

4.2.1. 单时钟周期

image
即每个时钟周期对应一次指令,周期很长。

而且,例如Store指令是不需要WB阶段的,但是周期却不能缩短(因为没法提前知道这个指令需要几级),这样有一小段时间被浪费了。

4.2.2. 多时钟周期

image
即每个时钟周期对应指令中的一级。

每级的时间不可能完全相同,所以周期只能取最大值。至于如何让每一级的周期差不多,这是流水线划分中完成的。

4.2.3. 流水线

image
流水线实现多个指令流水。

这样Throughput最高。不过指令中无论有无空闲级,即使这一级不需要工作,也需要让其空跑把时间耗掉(也即NOP),否则可能会出现流水线混乱。

4.3. 流水线的优点

  • 完成单条指令的延时增加了,完成指令的Throughput却大大提高了,整个程序(多条指令)的完成时延大大减少了。

  • 流水线上每级完成的任务都相对简单,通过任务分解大大减低了复杂逻辑设计的难度。

4.4. 流水线需要考虑的问题

  • 延时的问题 (实时)

  • 数据竞争冒险的问题 (Hazard)

  • 分支程序流的问题 (Predication)

  • 逻辑复杂度的问题 (Scheduling)

  • 功耗的问题 (低功耗或高能效)

4.5. 流水线的分类

4.5.1. 按线性与否分类

  • 线性流水线(Linear Pipelining)

    每一个流水段都流过一次,而且仅流过一次。

  • 非线性流水线(Nonlinear Pipelining)

    在流水线的某些流水段之间有反馈回路或前馈回路。

  • 不同流水线的表示方式

    线性流水线能够用流水线连接图唯一表示,非线性流水线必须用流水线连接图流水线预约表等共同表示。
    image

4.5.2. 按流水线的流水对象分类

  • 宏流水线

    多处理器间的流水线。

  • 处理机级流水线/指令级流水线

    处理器指令流水线。

  • 部件级流水线/操作流水线

    针对某一个小部件,例如浮点加法运算。

4.5.3. 按照流水线的功能分类

  • 静态流水线

    同一段时间内,多功能流水线中的各个功能段只能按照一种固定的方式连接,实现一种固定的功能。

    只有连续出现同一种运算时,流水线的效率才能得到充分的发挥。

  • 动态流水线

    在同一段时间内,多功能流水线中的各段可以按照不同的方式连接,同时执行多种功能。

    例如浮点加法与定点乘法。

4.6. 流水线性能分析

4.6.1. 计算吞吐量(Throughput)

image

(其中\(k\)为流水线级数,\(\Delta t\)为流水线时钟周期,\(n\)为任务数,\(S_k\)代表流水线的级别)

吞吐量定义为:任务数/执行这些任务的时间$,也就是单位时间内执行的任务数量。对于一般的流水线,从时空图容易得到:

\[TP=\frac n{(k+n-1)\Delta t} \]

其理论极限为:

\[\lim_{n\to\infty} TP=\frac1{\Delta t} \]

也就是说,在任务量无穷大时,可以认为所有任务完全并行,因为装入和排空时间相较于整个中间过程来说可以忽略。

4.6.2. 计算加速比(Speedup)

加速比定义为:加速前的时间/加速后的时间,在这里也就是:

\[S=\frac{kn\Delta t}{(k+n-1)\Delta t}=\frac{kn}{k+n-1} \]

其极限为:

\[\lim_{n\to\infty}S=k \]

根据公式看,当流水线段数增加时,加速比会提高,但是达到加速比极限需要的任务量也更多(因为装入和排空过程更长了)。

实际上,流水线级数并不是越多越好,原因前面已经阐述过了。

4.6.3. 计算流水线效率(Efficiency)

流水线效率定义为:流水线占据的时空区/总的时空区,换句话说是流水线时空区占满的程度

\[E=\frac{kn\Delta t}{k(k+n-1)\Delta t}=\frac n{k+n-1} \]

其极限为:

\[\lim_{n\to\infty}E=1 \]

4.6.4. 计算性价比(Performance Cost Ratio)

在实际考虑流水线时,不单单考虑效率,也要考虑价格。将二者合并考虑,就是性价比。

顾名思义,其定义为:性能/价格

很多时候,流水线级数增加,性能永远是提高的,但是价格也会提高,所以性价比是有最优值的。

4.6.5. 流水线不均匀的情况

不均匀,也就是指各级的周期相差很多。
image
如果还是选择取最大值的话,就是以上的时空图,浪费的部分太多,throughput太低。

解决办法:

  • 如果可以的话,将时间较长的“瓶颈”级继续细分。

    比如上述的\(S_2\)是三倍时间,可以尝试将其再分为三级。

  • 将“瓶颈”级并行设置。

    比如上述的\(S_2\)是三倍时间,就可以将该级并行设置三段。

    image

4.7. 高级流水线技术

单/多发射的概念和标/超标量的概念我觉得有些模糊。

4.7.1. 单发射处理机(Single-Issue)

每个周期只取一条指令、只译码一条指令,只执行一条指令,只写回一个运算结果。取指令部件和指令译码部件各设置一套;只设置一个多功能操作部件或设置多个独立的操作部件;操作部件中可以采用流水线结构,也可以不采用流水线结构。

目标是每个时钟周期平均执行一条指令,指令级并行度(ILP, Instruction-Level Parallelism)的期望值为1。

image

4.7.2. 多发射处理机(Multiple-Issue)

每个周期同时取多条指令、同时译码多条指令,同时执行多条指令,同时写回多个运算结果。需要多个取指令部件,多个指令译码部件和多个写结果部件。设置多个指令执行部件,有些指令执行部件采用流水线结构。

目标是每个时钟周期平均执行多条指令,ILP的期望值大于1。

image

多发射是有一些问题需要仔细考虑的。

4.7.2.1. 多发射机中的竞争-冒险

在多发射处理机的同一个发射槽(同时发射的一组打包指令)中,如果调用的数据存在因果关系,那么多发射就会存在竞争-冒险问题。给出一个RISC-V指令集的例子:

ld x31, 0(x20)
add x31, x31, x21

在单发射机中,x31ld之后才add,因此x31的值是最新的。

如果这两个指令在一个槽中,那么其中x31的值还未被ld就被add了,因为ld指令要到WB阶段才算得到输出,而add在EX阶段就已经将加数传入ALU,x31的值是来不及更新的。

因此,多发射机中选择哪些指令打包在一个槽里面,是需要编译器仔细处理的。

4.7.2.2. 多发射机中的循环展开(Loop Unrolling)

在循环语句中,有些出现在循环体里的变量,看似是一个变量,实则毫无关系。

先用常见的C语言举个例子,假设有一个处理向量交换的循环:

/* vector exchange: a,b */
float temp;
for(i=0;i<len;i++){
  temp = a[i];
  a[i] = b[i];
  b[i] = temp;
}

程序给我们的直接信息是:向量的每个单元都通过变量temp作中介来交换。但是仔细想想,每个循环中的temp有关系吗?答案是,完全没有。我们根本不在乎它是temp还是别的什么名字,只要他是一个空的float数就可以。

所以在并行编译后呈现出的结果是这样的:

/* vector exchange: a,b */

// thread 1
float temp1;
temp1 = a[i];
a[i] = b[i];
b[i] = temp1;

// thread 2
float temp2;
temp2 = a[i];
a[i] = b[i];
b[i] = temp2;

...

其中不同的thread是可以同时发射的。如果拘泥于使用同一个temp变量,那么是无法执行多发射的。而进行循环展开之后,就可以进行多发射。

但是代价就是使用了更多的float数,这也能看出来是一种“用资源换取效率”的方法。

下面给出一个RISC-V的例子:

Loop:ld x31, 0(x20) //x31=array element
add x31, x31, x21 //add scalar in x21
sd x31, 0(x20) //store result
addi x20, x20, -8 //decrement pointer
blt x22, x20, Loop //compare to loop limit
//branch if x20>x22

其中前三行的x31寄存器仅仅只是一个中介:用来放ld的数据,计算一下之后又sd回到Memory当中。所以这里的x31名字也是可以改变的,容易进行循环展开。


标量/超标量、流水线/超流水线的理解:
前者针对的是指令部分,也即每次发射多个指令。后者针对的是整个流水线,也即每个周期发射多次指令。

4.7.3. 标量流水线(Scalar)

一条指令流水线,一个多功能操作部件。

每个时钟周期平均执行指令的条数小于1。

4.7.4. 多操作部件流水线

一条指令流水线,多个独立的操作部件(可以采用流水线,也可以不流水)。

多操作部件处理机的指令级并行度小于1。

4.7.5. 超标量流水线(Superscalar)

多条指令流水线。多个独立的操作部件。先进的超标量处理机有:定点处理部件CPU,浮点处理部件FPU,图形加速部件GPU。

超标量处理机的ILP大于1。

4.7.6. 超流水线

在一个周期内能够分时发射多条指令的处理机。

4.7.7. 超标量超流水线

略。

4.7.8. 指令级并行处理机的比较

性能(相较于普通标量机):

image

4.8. 多流水线调度

顺序(in-order)发射(issue)/乱序(out-order)发射、顺序完成(completion)/乱序完成。

常见的有3种组合:顺序发射顺序完成、顺序发射乱序完成、乱序发射乱序完成。

简而言之,顺序就是按照代码的顺序执行指令;乱序就是按照指令之间的依赖关系重新调度,以期获得更高的利用率。乱序的执行效率高,但是设计难度更大,编译器更复杂,具有一定风险。

4.9. 流水线的冲突(Pipeline Hazards)

流水线冲突 (Pipeline Hazards)是指对于具体的流水线来说,由于指令相关导致的流水线资源冲突。使得指令流中的下一条指令不能在指定的时钟周期执行。

4.9.1. 冲突的分类

4.9.1.1. 结构冲突

硬件资源满足不了指令重叠执行的要求而发生的冲突。

例子1:五段流水机中,IF是访问Memory(Instruction Memory),而MEM是访问Memory(Data Memory)。好像这也没有冲突?但实际上,在现代的大多数处理器中,Instruction Memory和Data Memory是不分的,所以是有可能存在冲突的。

例子2:ID和WB都是访问寄存器堆栈的。但是其解决方法也是很直观的:ID是寄存器输出,WB是寄存器写入,其实还没有完全冲突。把写操作安排在时钟周期的前半拍完成,把读操作安排在后半拍完成,就可以解决问题。

4.9.1.2. 数据冲突

也就是,某一数据值未得到更新。

下一条指令需要某一个数据时,该数据在上一条指令中还未计算出来,因此其值是旧值。

image

其有3种类型:RAW、WAR、WAW。为什么没有RAR?因为读入操作没有改变存储的数据,不会产生冲突,必须有写操作才有冲突的可能性。

指令\(i\)的R在ID结束时刻完成,这之后对Reg的操作无法影响已经读出的数。

指令\(i\)的W在WB结束时刻完成,这之后对Reg读才能读到最新的值。

指令\(i+1\)的R、W同理。

  • 数据相关/真相关(RAW, Read After Write)

    顾名思义,这种相关才是真正有影响的相关。根据之前的分析,RAW的R指令在W之后,但是在流水线中W比R还迟约两个周期,因此存在冲突。

  • 反相关(WAR)

    W在R的指令之后,因此本来设计时候就不认为R的值应该是W完成之后的值。比如:对于C代码b=2;a=b;b=3;,其中a=b;b=3就是WAR。但是这段代码,写的时候我们就知道应该得到a=2,所以这个结果不是意外情况,不能认为是冲突

    但是假如编译器因为某种缘故,将代码的顺序调整了,那么WAR就有可能出现冲突

  • 输出相关(WAW)

    因为在五段流水线中,对Reg的写操作仅在WB阶段,所以位于后面的指令的W必定比位于前面的指令的W要迟,应该是不存在冲突的。

    但是仅限我们知道的五段流水线。假如某个流水线中有多个可以写的阶段,那么是有可能存在冲突的。

    同样地,假如编译器因为某种缘故,将代码的顺序调整了,那么WAW就有可能出现冲突

4.9.1.3. 控制冲突

也就是,控制器的PC值未得到更新。

如果上一条指令中存在分支命令,那么其在EX阶段后才能给出分支指令地址。但此时下一条指令已经进行了一部分(已经过了IF阶段),无法再更换指令地址。

4.9.2. 冲突的后果

  • 导致错误的执行结果。
  • 流水线可能会出现停顿,从而降低流水线的效率和实际的加速比。
  • 当一条指令被暂停时,在该暂停指令之后流出的所有指令都要被暂停,而在该暂停指令之前流出的指令则继续进行(否则就永远无法消除冲突)。

4.9.3. 冲突的消除

4.9.3.1. 插入NOP

简而言之,就是插入一个空指令,让处在后面的指令可以“等一下”,而不至于与之前的指令冲突。

image

  • 插入Stall

这个方法与插入NOP其实是等价的。

image

4.9.3.2. 指令调度

编译器在编译时重新组织指令顺序来消除冲突。

编译器会判断存在依赖关系的指令,并修改它们的顺序。这一点对于reg变量相对容易操作,但是对于memory变量来说较难,因为指令集(比如RISC-V)都是支持mem地址偏移的。比如ld r1, 100(r4)ld r1, 0(r6)读到的可能是一个mem,只要r6-r4==100即可。但是这对于编译器来说是难以判断的。

4.9.3.3. 定向技术

也称作旁路(by-passing)、短路、前递(forwarding)。

其核心思想是:在某个数据实际产生的一瞬间就立刻将其送到下一条指令内部,而不是等待上一条指令完成。下图分别展示了改进前后的数据通路。

image
image

对于R类型指令(比如addsubor等)而言,按照一般的流程,寄存器数据要等到WB结束后才算更新完成,但这太迟了。改用定向技术之后发现,R类型运算其实在EX阶段结束(也即ALU输出)就有了寄存器的对应数据,只是还没有送到对应的寄存器中,将其直接送到下一指令的EX之前,是正好的,这也是定向技术能处理的最极限情况。

在这种情况下,旁路的开启与否其实是编译时就已经决定了的,编译器根据代码间的依赖关系决定的。如果等待运行指令时再临时决定,那也是来不及的,因为运行指令必须直到WB阶段才知道存到哪个寄存器里面,才知道依赖关系。

但是定向技术仍然有局限。如果前一条指令时load指令引起了RAW冲突,那么定向技术还是来不及。load指令要取得数据至少在Mem阶段之后,如下图所示。

image

这种时候就要结合前面的调度或者Stall方法:当检测到“RAW,且前一条为load指令”时,重新调度或者插入Stall来规避冲突。

4.9.3.4. 分支预测

这是一种用于解决控制冲突的方法。控制冲突当然也可以用插入Stall的方法解决。

分支预测方法就是:不论分支的实际走向如何,总是先为它作出相应的选择:失败或成功。

  • 预测分支失败

    也就是预测分支永不跳转。

    如果预测错误,那么就等于插入了本就需要的Stall,只不过Stall不是完全空的,没有什么损失;如果预测正确,那么就可以节省下Stall的时间。

    image

    • 实际上分支失败

      预测正确,此时分支语句与普通语句是一样的,没有影响到流水线。

    • 实际上分支成功

      预测错误,此时分支需要跳转,那么分支语句之后的若干条语句将会作废(等于是插入了Stall),之后正常跳转。

      不用担心作废的指令会对已有数据造成不可逆的改变。因为分支判断结果是在指令\(i\)的EX结束时就得到了,此时之后的指令都还未进行到MEM和WB阶段,不会对已有数据造成实质性改变。

  • 预测分支成功

    也就是预测分支永远跳转。

    在五段流水线的前提下,预测分支成功与预测分支失败正好相反,没有任何提升,反而可能会有损失。

    image

    • 实际上分支成功

      预测正确,此时分支跳转,那么机器正常跳转(即重新从IF开始)。

    • 实际上分支失败

      预测错误,此时分支不跳转,那么机器已经跳转后还需要再度跳转回来。

  • 延迟分支/延迟槽

    即通过调度,将一个与分支指令不存在冲突的指令填在本来是Stall的位置上,来掩盖Stall的占用时间。

    打个比方,就好比是第一门考试结束之后,成绩还没有出来,在这个时间里面赶紧复习下一门考试。两门考试之间是没有依赖关系的,将第二门考试的复习放在这里进行,只是为了节约时间、增加效率,不然还一直坐着等着第一门成绩出来吗?

    而这种方法势必要让编译器找到合适的指令放在延迟槽里面,根据调度源的不同又可以分为:

    • 从前调度
    • 从目标(成功)处调度
    • 从失败处调度

    image

    这里的从目标/失败处调度,与前面的预测分支成功/失败有什么区别呢?


    调度是把那个位置的一条指令复制过来,可能会增大程序的代码量。而预测成功/失败是指指令在分支结果未出之前就已经将下一条指令的PC改了(也即IF的内容被改了),是修改了代码执行的顺序。

4.9.3.5. 寄存器重命名

在某些情况下,不同行指令中的寄存器看起来名字一样,但实则没有关系(类似于前面在循环展开中提到的)。

如果只是将数据从寄存器中取出,进行一些计算后又存回存储器,那么这个中间变量的名字是不重要的,因为只是临时存储。比如:

Loop: L.D F0,0(R1)
ADD.D F4,F0,F2
S.D 0(R1),F4
L.D F0,-8(R1)
ADD.D F4,F0,F2
S.D -8(R1),F4
Loop: L.D F0,0(R1)
ADD.D F4,F0,F2
S.D 0(R1),F4
L.D F6,-8(R1)
ADD.D F8,F6,F2
S.D -8(R1),F8

其中的F0寄存器正是如此。


实际上,调度也不只是为了解决冲突:只要是能消除潜在冲突、或者提高性能的调度,都是可用的。

总结调度的一般步骤:

  1. 调整次序(减少分支预测延时、冲突)
  2. 打开循环
  3. 重命名寄存器(避免寄存器冲突)

4.10. 软件流水

如果循环体中每次迭代都是独立的,那么可以从不同的迭代中找到并行的指令来执行。软件迭代就是从原循环中的不同迭代中找出指令形成一次迭代。

image

比如上图中,每一列都是一个循环内部的阶段任务,每一行是同一时间执行的命令。最后可以发现,组成的新循环体是包含了不同循环序号、不同阶段的任务的。

软件流水往往被认作是循环展开loop unrolling的上位替代,因为其不需要实际展开,也就不会实际地增加代码量(也即编译后的程序大小)。

5. 存储器

5.1. 存储器分类

  • 主存

    • RAM

      • SRAM

      • DRAM

      一般来说,DRAM的结构更简单,面积远小于SRAM,集成度远高于SRAM,成本远低于SRAM。DRAM的速度慢于SRAM,因为前者是利用电容存储的,其充放电较慢;后者则是利用触发器存储,要快一些。

      DRAM需要周期性刷新,存在一定的错误率,且有死时间。

    • ROM

      • MROM

        Mask ROM,掩膜ROM,一次性写入,无法更改。

      • PROM

      • EPROM

      • EEPROM

      • Flash ROM

  • 外存

    • Disc

      一般指机械硬盘。在盘上有圆圈形刻痕来记录数据,磁头可以寻找数据位置并读写数据。

    • Floppy Disc

      即软盘驱动,现在已经被淘汰。

    • CD

    • Flash

      在这里指固态硬盘。其是利用浮栅MOS原理制作的一种EEPROM。所有的EEPROM的写操作都会造成磨损,包括Flash。所以Flash中一般存在管理单元,这些单元会屏蔽已经失效的部分,并使得存储内容在各部分均匀分布,使得损耗均匀。

      Flash主要分为NAND和NOR逻辑两种类型。

5.2. 存储层次

5.2.1. 计算机对于存储的需求

  • 高速度
  • 大容量

但是这两者是冲突的。高速度要求存储器的存取时间短,因此面积不能很大,容量也就不会大。而太大的存储器,因为存取路径长而导致数据存取慢,并且存储单元过多导致驱动负载太大。

5.2.2. 计算机的存储层次

image

在计算机中,存储是有层次的。简单来说,距离计算机CPU越近的存储器,其速度越快、价格越高、容量越小、越常用。

Registers的寻址通常由指令完成(指那些以Reg为基础建立的指令集)。这种寻址是直接的,尽管之前说了很多(比如Loop Unrolling, Renaming)等跟寄存器指令有关的内容,但是那只是编译器的工作,计算机得到的寻址指令是直接的。

Memory通常是通过寄存器值寻址的。Memory可以用来存储数据和指令(Data Memory和Instruction Memory,不过有时候二者是不区分的)。

Disc/Tape等更大容量的存储往往是通过寄存器值寻址的,通常它们都是分成“块”,按块存取。


下面分别介绍不同层次的存储。


5.2.2.1. Register File

一般,寄存器都是以寄存器堆栈(Register File)形式出现。

一种常见的Register File使用触发器来构建。

image

image

简而言之,就是利用译码器对地址进行译码,然后寻址找到对应的Register,然后对其进行读写操作。

5.2.2.2. Cache

Cache其实是介于Register和Memory之间的缓冲,这也是“缓存”一词的由来。Cache往往还会分为若干级,其中需求速度较高的可用SRAM(同时成本高、容量小),需求速度不高的可用DRAM。

Cache内部往往还被分成若干个Set。

因为Register只是处理当前指令,而很多时候指令的走向是容易预测的(比如按顺序执行),所以Cache常用来存储接下来的若干条指令或数据。

  • L1 Cache

image

  • Caches inside CPU

image

从图中可以看到,在五段流水线中学习的Data Memory,虽然名为Memory,但实际上是L1 Cache。

所以实际上,某个数据存储属于什么层次也并不是固定死的,要根据容量、常用性等等作出抉择。

5.2.2.3. Main Memory

Memory有两种存储方式。

  • 一层物理地址

    image

    Memory的每个单元都有唯一的物理地址。对于\(N\)位地址,可以分配给\(2^N\)个单元。处理器将地址存储在Address Register(也有可能是Cache)里面,由此进行寻址。

  • 二层物理地址

    image

    \(N\)位物理地址被分成了两段,第一段是块的序号,第二段是位移,也就是在块内部的序号。地址往往存储在Address Cache中。

5.2.2.4. Disc

image

如图所示,Disc内部的数据是分块存储的。块的大小是预先指定的(比如硬盘分区),如果文件小于块大小,那剩余空间不会被利用;如果文件大于块大小,文件可以被分割存储。

这些任务都是由文件系统进行的,它负责处理数据在存储器中的位置(物理地址)与在操作系统中的位置(文件路径),并建立对应的映射关系。

image

Disc及其它IO设备都有预留的地址在Address Memory中,这些地址寻址由Memory完成,不需要复制到Register中(这一特性称为Non-Cacheable)。

Disc会被分为多个Page,以Page序号作一级地址来存储数据。类似于前面将Memory分成Block的操作。

5.2.3. CPU-Memory瓶颈

CPU的处理速度增长很快,但是Memory(例如DRAM)的速度则远远跟不上。为此只能通过增大带宽(数字电路的带宽,即指数据吞吐量)的方式来提高传输速度。

  • 延迟

    CPU访问存储一定是需要延时的,而这个延时远比处理器的核心时钟周期长。

  • 带宽

    因为一次指令中可能有多次存取阶段,数据需求量是远大于1/cycle的,带宽必须足够。

5.2.4. 存储器的Locality

也即一个核心思想:存储器中的小部分内容占据了大部分的运行时间。所以要将常用的数据放在更快的存储器内,当索引不常用数据时,先在高层次存储中寻找,找不到再向下一层寻找。

下图给出了一种利用Locality来合理分配数据层次的例子。

image

5.2.4.1. Temporal(时间的) Locality

当程序存寻找某个地址,那么很有可能它会多次寻找该地址。

一个典型例子就是循环。因此,当程序存取某个数据后,先将其继续保留在Register中(正常的步骤是:变量在Store操作后,Register中的对应值就可以释放),以防止程序连续存取该数据。

但是这样也有风险。

  1. 寄存器未必足够
  2. 寄存器并不是总能寻址到的(比如只知道指针)
  3. 万一存储器的数据又被更新了,寄存器中的值就不是最新值。

5.2.4.2. Spatial(空间的) Locality

当程序寻找了某个地址,那么很有可能它会寻找邻近地址。

容易想到,典型的例子就是顺序执行的程序指令。在程序中,诸如数组、指针、结构体等变量往往是存储在相近的物理地址的。因此,当它们中的某一部分被寻址时,就可以将它的所有内容读出来放在寄存器中备用。

5.3. 虚拟存储

虚拟存储,是将Disc/Flash的一部分作为Cache的扩展。本质上可以理解为一种Cache。但是考虑到其来源,虚拟地址的分块往往还是称为Page。

5.3.1. 诞生原因

  1. 防止Main Memory不够大,而导致较大程序不能正常运行。
  2. 允许多个程序之间共享内存,比如虚拟机、云服务器。

现在第一条往往已不成为问题,设计虚拟存储是为了第二条。

5.3.2. 虚拟地址

简而言之,虚拟地址就是Main Memory将Memory重新编址。对于某些虚拟地址而言,其有对应的物理地址,因为这些存储Page在Memory内部;反之,有一些虚拟地址找不到对应的物理地址,只能使用虚拟地址,因为这些Page指向的是Disc/Flash。

6. 指令并行

指令并行是一种指令多发射,根据编译器对流水线的调度,在循环体间并行执行。

6.1. 调度方法

6.1.1. 流水线静态调度

依靠软件(编译)寻找并行性,在指令执行前调度。

6.1.2. 流水线动态调度

依靠硬件寻找并行性,在指令执行中调度。

6.2. Scoreboard Control

本例的来源是棒球记分牌。棒球的规则与别的球类运动不太一样。比如足球、篮球是比谁的得分多;乒乓球、羽毛球是比谁先打到指定分数:它们的计分方式都算简单。棒球不同,大致规则是:每次的操作会导致场内换人或有人出局(即资源被消耗),如果资源被消耗殆尽(机会用完了),就会得分或者失分。

而棒球记分牌的理念就引向了一种指令级并行的思路:提前检测可能出现的冲突(即资源是否被占用),在无冲突、无依赖性(not dependent on previous instructions and no hazrds)的情况下完成调度,实现指令并行。

比如用Scoreboard的方法去控制一个算数运算模块(包括int和FP计算)。

image

6.2.1. Scoreboard阶段

Scoreboard的实现方式一般是顺序发射、乱序执行。乱序执行将ID阶段分为两段:

  1. 发射:指令译码,检查有无结构冲突。
  2. 读操作数:确定没有冲突之后,读操作数。

所以Scoreboard总共被分为4个阶段。需要指明的是,在Scoreboard中不考虑使用forwarding来解决冲突,冲突都是依靠调度来解决。

  1. 发射(Issue):指令译码,检查有无结构冲突

    顺序发射。

    检查结构冲突,如果有,暂停发射。

    检查数据WAW冲突,如果有,暂停发射。这里要考虑WAW,因为指令是乱序执行的,代码顺序可能被调换,因此需要检查WAW。

  2. 读操作数(Read operands):确定没有冲突之后,读操作数

    检查数据RAW冲突。在之前我们说过,关于RAW冲突,在没有forwarding的情况下,只能考虑插入Stall,这里就是这么做的。

  3. 执行(Execution):按照操作数进行计算

  4. 写回结果(Write result):结束执行,写回数据

    检查WAR冲突。如果存在,则暂停写回。

6.2.2. Scoreboard状态

  1. 指令状态

    即指令进行到哪个阶段了。

  2. 功能单元(Functional unit)状态

    即FU处于什么状态,这些状态指示了后面的指令是可以插入并行还是等待。总共有9个方面:

    Short Name Full Name Desciption Note
    Busy / 表征FU是否busy 若为No,则允许使用该部件(比如add,multi等),否则必须等待
    Op Operation FU中执行何种命令 执行命令的名称(比如add,multi等)
    Fi FP Reg, Destination 目标寄存器 记录将要写向哪个寄存器(比如add x1, x2, x3)
    Fj, Fk FP Reg, Source 源寄存器 记录数据来源的寄存器(比如add x1, x2, x3
    Qj, Qk Quote from FU Fj, Fk的寄存器数据来源何处 如果源数据来源于无冲突寄存器,则不用记录;如果存在冲突,需要记录,直至冲突解除才能读入寄存器数据
    Rj, Rk Ready Flag 表征Fj, Fk ready的标志 只有在寄存器的数据来源可靠(即Qj, Qk已经解除)的情况下才能ready,将数据传入EX阶段后立刻复位成not ready
  3. 结果寄存器状态

    指示FU中的哪些部分需要写回。

6.2.3. Scoreboard示例

过程太长,这里不放了。给出一张示意图,展示如何用图分析。

image

6.2.4. Scoreboard的缺点

  1. 顺序发射,这使得Scoreboard发现指令并行的程度可能还不够高。因为就算有冲突,指令依然会顺序发出,然后Stall等待。

  2. Stall,对于WAW和RAW,都只能用Stall来处理。

6.3. Tomasulo's Algorithm

6.3.1. 改进的地方

  1. 为Function Units增设Buffer,称为Reservation Stations。FU状态存在RS里面,包括:

    Short Name Full Name Desciption Note
    Busy / 表征FU是否busy 若为No,则允许使用该部件(比如add,multi等),否则必须等待
    Op Operation FU中执行何种命令 执行命令的名称(比如add,multi等)
    Vj, Vk Value of source operands 源数据计算的值 当Qj, Qk所求数据计算完成后,进行重命名避免冲突,重命名后由Vj, Vk记录
    Qj, Qk Quote from FU 寄存器数据来源何处 如果源数据来源于无冲突寄存器,则不用记录;如果存在冲突,需要记录,直至冲突解除才能读入寄存器数据
  2. 指令中的寄存器都不再是寄存器,而是RS内容,或指向RS内容的指针

    方便寄存器重命名,可以解决WAR,WAW冲突。

  3. 当结果从RS写回FU中,只能通过Common Data Bus,这个总线同时能够通知所有FU有哪些值被修改了

6.3.2. Tomasulo示例

过程太长,这里不放了。给出一张示意图,展示如何用图分析。

image

6.3.3. Tomasulo的优势

  1. 重命名寄存器消除了冲突

  2. 消除了为WAW和WAR插入的Stall

6.4. 多发射(Multiple Issue)

就是每次打包数条指令同时发射。

7. 分支预测

之前的指令并行没有考虑分支指令。但是实际上分支指令带来的惩罚是很高的。所以有必要对分支进行预测。

image

7.1. 减少分支惩罚的方法

7.1.1. 软件角度

  • 通过loop unrolling来避免分支。这样做会导致代码长度增加。

  • 通过重新规划分支,来让分支计算结果尽可能早地出现,以减小惩罚。

7.1.2. 硬件角度

  • 进行指令填充。即将分支计算中等待的bubble用有效指令填充,而不是让其空跑浪费掉。

  • 分支预测。预先预测分支的走向:如果成功,能节省大量的时间;如果失败,会遭受一定的惩罚。

    分支预测需要保持较高的准确性,否则没有意义。现在分支预测的正确率可以达到95%以上。

    分支预测在失败的时候要将预测进行的语句退回,因为这个语句本不该出现。

7.2. 分支预测的实现

7.2.1. 静态分支预测

为每个分支预设一个概率值,是固定的。预测时,根据设定概率进行分支预测。

image

可以预见的是,这种预测方式对于不同特点的代码,成功率差异很大。

7.2.2. 动态分支预测

预测的成功与否将作为反馈值影响预测器之后的预测。换句话说,动态预测器可以根据过往的预测历史进行调整。这在很多时候是很有效的,比如对于一个循环体,往往是循环了很多次以后才会跳出循环,那么中间循环的多次都是成功预测。

image

7.2.2.1. 1位预测器

1位预测器,就是用1位来记录上一次分支的状态。

image

7.2.2.2. 2位预测器

2位预测器,就是用2位来记录过往历史。

  • 状态机2位预测器

    总共有4种状态:11、10、01、00。每当分支没有执行,就减1,反之加1;当状态为11、10时预测分支执行;反之预测分支不执行。这样会有两次预测错误的机会。

    为什么2位预测有优势?因为在循环体中,跳出循环只有一次。所以,2位预测在循环体中只会预测错1次:当本次是最后一次进入循环体,而下一次跳出循环体时。而对于“本次跳出循环体,下一次回到循环体中”的情况,2位预测器能正确预测,但1位的不行。

    image

  • Branch History Table(BHT)

    利用多个2位存储对历史进行记录,并指导预测。该方法能达到80~90%的成功率。

    image

  • Branch Target Buffer(BTB)

    BTB能通过缓冲预先知道:本次指令是否是分支指令,如果是,那么目标地址是什么。

    image

7.3. 多线程(Multithreading)

7.3.1. 指令并行中的概念

  • 线程Thread

    线程往往指较容易的任务,在同一个程序中并行执行的。多个线程往往共享存储空间。例如:GPU、CPU多核。

  • 进程Process

    这个概念比thread大,往往具有独自的存储空间和系统状态。在进程之间切换往往需要操作系统(比如电脑切换程序)。

7.3.2. 多线程的实现方式

7.3.2.1. 硬件多线程

多个线程共享处理器中的同一个处理单元。

每个线程具有各自的PC、寄存器、存储空间。多线程之间通过寄存器值的复制交流来联系。

线程的切换需要用到context,这是相对较快的过程,一般需要不超过数个时钟周期。

7.3.2.2. 细粒度(Fine-Grained)多线程

context切换时间更短,甚至只需要一个时钟周期。

多个线程的执行可能重叠,空闲的线程可以被跳过来节省时间。

7.3.2.3. 粗粒度(Coarse-Grained)多线程

context切换较慢,需要在线程空闲时间切换。

当空闲存在时,需要暂停流水线等待。

7.3.2.4. 同步(Simultaneous)多线程

可以将不同线程中的部分拼接成新的线程,从而不需要切换,因此可以最大化利用流水线。

8. 多处理器系统

8.1. 多处理器系统的互连结构

image

两个概念:

  • WSC(Warehouse Scale Computers)系统中多处理器通常由自己的存储地址空间,即它们之间不共享存储器,这样的架构便于进行扩展。

  • HPC(High Performance Computing)系统中多处理器通常共享存储器,这种共享的存储器可以达到更高的性能。

8.2. 多处理器系统的架构

8.2.1. 对称式

对称共享存储器多处理器或集中式共享存储器多处理器SMP(Symmetric Multi-Processor)中一般处理器核心的数目比较少,因此可以共享一个集中式的存储器。

SMP有时也被称为一致存储器访问UMA(Uniformed Memory Access)多处理器。UMA的所有处理器访问存储器的延时是一致的。

image

8.2.2. 分布式

在分布式共享存储器DSM(Distributed Shared Memory)也称为非对称存储器NUMA(Non-uniformed Memory Access)多处理器系统中,存储器的访问时间取决于数据在存储器中的位置。

image

8.3. 共享内存的一致性和连贯性

8.3.1. 一致性(Coherence)

一个共享数据可能有全局状态(在主存储器中)和多个本地状态(在缓存中),如果一个读操作每次都返回该数据的最新写入值,那这个存储系统是一致的。一致性的问题反映了读操作可能返回什么值。

8.3.1.1. 一致性的条件

储系统是一致的,如果以下三个条件都满足:

  1. 处理器P读取位置X,在此之前是由P对X进行写入,在P执行写入和读取之间没有其他处理器对位置X进行写入
  2. 一个处理器向存储器位置X执行写入操作之后,另一个处理器读取该写入值,而且在两次访问之间没有其他处理器向X写入
  3. 在任何一个处理器看来,任意两个处理器对同一位置执行的写操作的顺序都相同

8.3.1.2. 一致性的实现

  • 目录式 (Directory)

    把某个物理存储器块的共享状态保存在一个目录里。在SMP里,这个目录是集中式的;在DSM里,这个目录是分布式的。

  • 监听式(Snooping)

    在SMP里,所有缓存都可以通过某种广播介质访问,所有的缓存都监听Snoop这一介质,以确定自己是否拥有该介质上所请求数据块的副本。总线是一种非常方便的广播介质。

8.3.2. 连贯性(Consistence)

一个共享数据可能有多个处理器写进多个Copy,一个处理器必须什么时候看见其他处理器的更新过的值,即一个处理器必须以何种顺序看见另一个处理器的写入操作,这就是连贯性的问题。

由于写入顺序必须通过读操作来观测,所以连贯性的问题是写入值什么时候被读操作返回。连贯性回答的问题是一个处理器在什么时候看见另外一个处理器更新的值,就是一个处理器什么时候看见另一个处理器的更新。

8.3.2.1. 顺序连贯性

顺序连贯性又称强连贯性,要求每个处理器都按顺序执行存储器访问操作。

实现顺序连贯性的最简单方法是要求下一个存储器访问必须在前一个存储器访问的所有操作完成之后完成。

8.4. 多处理器中的互连网络

由开关元件按一定拓扑结构和控制方式构成的网络以实现计算机系统内部多个处理机或多个功能部件间的相互连接。

可以是同步/异步通信,可以是集中/分布控制。

8.4.1. 多处理器的交换方式

8.4.1.1. 电路交换(Circuit switching)

源结点和目的结点之间的物理通路在整个数据传送期间一直保持连接。

8.4.1.2. 分组交换(Packet switching)。

把信息分割成许多组(又称为包),将它们分别送入互连网络。这些数据包可以通过不同的路径传送,到达目的结点后再拼合成原来的数据。结点之间不存在固定连接的物理通路。

8.4.2. 总线

比如AXI总线。

image

8.4.3. 静态网络

8.4.3.1. 静态网络的特点

静态网络由点—点直接相连而成,这种连结方式在程序执行过程中不会改变。

如果用图来表示,结点代表开关,边代表通信链路。

8.4.3.2. 静态网络的指标

  • 结点度
    与结点相连接的边(链路或通道)数,表示节点所需要的I/O端口数,模块化要求结点度保持恒定。根据通道到结点的方向,结点度可以进一步表示为:

    \[结点度=入度+出度 \]

    其中入度是进入结点的通道数,出度是从结点出来的通道数。

  • 距离

    与两个结点之间相连的最少边数。

  • 网络直径

    网络中任意两个结点间距离的最大值。

  • 网络规模

    网络中结点数,表示该网络功能连结部件的多少。

  • 等分宽度

    某一网络被切成相等的两半时,沿切口的最小边数称为该网络的等分宽度。

  • 结点间的线长

    两个结点间的线的长度。

  • 对称性

    从任何结点看,拓扑结构都一样。

8.4.3.3. 典型的静态网络

  1. 线性

    image

    对N个结点的线性阵列,有\(N-1\)条链路,直径为\(N-1\)(任意两点之间距离的最大值),度为2,不对称,等分宽度为1。\(N\)很大时,通信效率很低。

  2. image

    对N个结点的环,考虑相邻结点数据传送方向:

    • 双向环:链路数为\(N\),直径\(\lfloor N/2 \rfloor\),度为2,对称,等分宽度为2。

    • 单向环:链路数为\(N\),直径\(N-1\),度为2,对称,等分宽度为2。

  3. 带弦环

    image

    对上图中12个结点的带弦双向环,

    • 结点度为3:链路数为18,直径4(比如红色结点),度为3,不对称,等分宽度为2。

    • 结点度为4:链路数为24,直径3(比如红色结点),度为4,对称,等分宽度为8。

  4. 树形

    image

    一棵K层完全二叉树应有\(N = 2K - 1\)个结点,对大结点度为3,直径为\(2(K - 1)\)(即右边任意一个叶子结点到左边任意一个叶子结点)。不对称,等分度为1。

    由于结点度为常数,所以树是一种可扩展的系统结构,比如这两种结构都可以缓解根结点的瓶颈问题:

    image

  5. 星形

    image

    星形实际上是一种二层树(如右图)。有\(N\)个结点的星形网络,有\(N - 1\)条链路,直径为2,最大结点度为\(N - 1\),非对称,等分宽度为1。

  6. 网格型

    image

    有N个结点的\(r\times r\)网(其中\(r=\sqrt N\)),有\(2N - 2r\)条链路,直径为\(r-1\),结点度为4,非对称,等分宽度为\(r\)

  7. 环形网(2D—Torus)

    image

    \(N\)个结点的\(r\times r\)网(其中\(r=\sqrt N\)),有\(2N\)条链路,直径为\(2\lfloor r/2\rfloor\),结点度为4,对称。

  8. 交叉开关

    image

    大大展宽了互连传输频带,提高了系统的效率;但交叉开关设备量过大,成本过高(一般\(n\leqslant 16\))。

8.4.4. 互连与通信

8.4.4.1. 基本术语与性能指标

  1. 消息、包和片

    消息(Message):是在多计算机系统的处理接点之间传递包含数据和同步消息的信息包。它是一种逻辑单位,可由任意数量的包构成。

    包(Packet):包的长度随协议不同而不同,它是信息传送的最小单位,64-512位。

    片(Flit):片的长度固定,一般为8位或更多。

    image

  2. 互连网络

    互连网络用来在多计算机系统的处理结点之间传递消息。互连网络的描述:

    • 拓扑(Topology)
    • 寻径算法(Routing)
    • 流控制(Flow Control)

    互连网络性能的两个重要指标:

    • 传输时延(Transmission Latency)
    • 吞吐量(Throughput)
  3. 传输时延与吞吐量

    一个消息的传输时延:从它在源结点进行发送初始化到它在目的结点完整的被接收所耗费的时间。

    一个网络的传输时延:在一定条件下发送消息的平均时延。

    网络的吞吐量:单位时间内网络所能传输的消息数目或长度。

  4. 传输时延的公式

    \[T=T_s+T_n+T_b \]

    \(T_s\)称为建立时延:一个消息在源结点和目的结点上装配和分解、从存储器拷贝到通信缓冲区以及正确性验证等所耗费的时间。它和机器本身的硬件、软件技术有关。

    \(T_n\)称为网络时延:消息头部从源结点进入网络到消息的尾部到达目的结点的时间间隔。

    \(T_b\)称为阻塞时延:消息传递过程中其他所有可能的时延(主要原因是资源冲突)。

8.4.5. 网络的寻径算法

8.4.5.1. 存储转发

当一个消息到达中间结点A时,A把整个消息放入其通信缓冲器中,然后在寻径算法的控制下选择下一个相邻结点B,当从A到B的通道空闲并且B的通信缓冲器可用时,把消息从A发向B。

缺点:每个结点必须对整个消息进行缓冲,缓冲器较大。网络时延与发送消息所经历的结点数成正比。

image

8.4.5.2. 虚拟直通

中间结点没有必要等到整个消息全部被缓冲后再作出路由选择,只要消息的目的信息域可用后,就可以作出路由选择。而通向下一结点的通道忙或结点的缓冲器非空闲时,必须把整个消息缓冲起来,这时和存储转发一样。

8.4.5.3. 线路开关

在传递一个消息之前,就为它建立一条从源结点到目的结点的物理通道。在传递的全部过程中,线路的每一段都被占用,当消息的尾部经过网络
后,整条物理链路才被废弃。

缺点:物理通道非共享;传输过程中物理通道一直被占用。

image

8.4.5.4. Wormhole

首先把一个消息分成许多片,消息的头片包含了这个消息的所有寻径信息。尾片是一个其最后包含了消息结束符的片。中间的片均为数据片。

片是最小信息单位。每个结点上只需要缓冲一个片就能满足要求。

用一个头片直接开辟一条从输入链路到输出链路的路径的方法来进行操作。每个消息中的片以流水的方式在网络中向前“蠕动”。每个片相当于Worm的一个节,“蠕动”以节为单位顺序的向前爬行。

image

优点:

  1. 每个结点的缓冲器的需求量小。
  2. 较低的网络传输延迟。
  3. 通道共享性好、利用率高。
  4. 易于实现Multicast和Broadcast。

9. 机器学习简介

9.1. 神经网络与生物神经的比较

具有相似的连接结构:

image

具有正反馈、负反馈:

image

9.2. 机器学习的映射函数

image

与数学函数不同的是,这里的输入输出可以是多种类型,比如:声音、自然语言、自然图像、策略等等。

9.3. 机器学习的三要素

9.3.1. 模型

  • 线性方法

    \[f(\mathbf x,\theta)=\mathbf {w}^T\mathbf{x}+b \]

  • 广义线性方法

    \[f(\mathbf x,\theta)=\mathbf {w}^T\mathbf{\phi(x)}+b \]

    如果\(\phi(\mathbf x)\)为可学习的非线性基函数,\(f(\mathbf x,θ)\)就等价于神经网络。

9.3.2. 学习准则

  • 期望风险:

    \[\mathcal{R}(f)=\mathbb{E}_{(\mathbf{x},y)\sim p(\mathbf{x},y)}[\mathcal L (f(\mathbf x),y)] \]

    损失函数就是用来表示\(y\)\(f(\mathbf x)\)之间的差距的,我们用函数\(\mathcal L (f(\mathbf x),y)\)来衡量。我们希望的是这个L函数最小化。常用的损失函数有0-1,平方,绝对值,对数等。

  • 经验风险:

    \[\mathcal{R}_D^{emp}(f)=\frac1N\sum_{n=1}^N\mathcal L (f(\mathbf x),y) \]

    期望风险通常是未知的,通过经验风险近似。经验风险就是所有的样本点都求一次损失函数然后进行累加后平均。在选择合适的风险函数后,我们寻找一个参数 ,使得经验风险函数最小化。机器学习问题转化成为一个最优化问题。

但是经验风险最小化原则很容易导致模型在训练集上错误率很低,但是在未知数据上错误率很高,称为过拟合。
image
期望风险与经验风险之差称为泛化错误。泛化错误可以衡量一个机器学习模型是否可以很好地泛化到未知数据。机器学习的目标是减少泛化错误。

9.3.3. 优化

让损失函数最小化。

一种常见的办法就是随机梯度下降法。在每次的训练当中,调整参数\(\theta\),使得损失函数不断减小,即使得损失函数沿\(\theta\)的梯度(导数)方向不断减小。

9.4. 感知机

image

image

9.4.1. 常用激活函数

  1. Sigmoid

    S型曲线函数,两端饱和。例如:

    • Logistic: \(\sigma(x)=\frac1{1+\exp(-x)}\)
    • Tanh: \(\tanh(x)\)

    image

  2. ReLU

image

9.5. 前馈网络

前馈网络中各个神经元按接受信息的先后分为不同的组,每一组可以看作一个神经层。每一层中的神经元接受前一层神经元的输出,并输出到下一层神经元。整个网络中的信息是朝一个方向传播,没有反向的信息传播。前馈网络包括全连接前馈网络卷积神经网络

image

而对于只有一层输入,一层输出,中间都是隐藏层的,称为深度神经网络

9.6. 卷积神经网络

9.6.1. 特征

  1. 预处理:经过数据的预处理,如去除噪声等。比如在文本分类中,去除停用词等。

  2. 特征(Feature)提取:从原始数据中提取一些有效的特征。比如在图像分类中,提取边缘、尺度不变特征变换特征等。

  3. 特征转换:对特征进行一定的加工,比如降维和升维。降维包括

    • 特征抽取(Feature Extraction)
    • 特征选择(Feature Selection)

image

一个典型例子,就是用卷积进行图像边缘提取:

image

9.6.2. 组成

以下是一个卷积网络的典型结构:

image

但是作为前馈网络,其存在一定的不足,比如:信息是单向的、输入输出尺寸固定等。所以有循环神经网络,通过自带的反馈神经元,能够处理任意长度的序列,目前应用广泛。

9.6.2.1. 卷积层

  • 卷积核:每个卷积层有一组卷积核来抽取特定的特征。

    卷积核可以是数核、函数核、矩阵核等等。

  • 特征图(Feature Map):输入,输出。

  • 卷积运算:积经常用在信号处理中,用于计算信号的延迟累积,当前时刻产生的信息可以和以前时刻延迟信息叠加。

    比如下图展示了二维矩阵卷积的算法。

    image

    但是需要特别关注矩阵边缘的卷积。有时可以拓宽矩阵边缘来保证卷积施加到了每个元素上;有时则不进行拓展,即不对边缘的元素进行处理。

9.6.2.2. 池化层

通过池化把特征图的尺寸变小。一般一个卷积层后跟一个池化层。

9.6.2.3. 全连接层

输入和输出全部连接。

9.6.2.4. 输出层

Softmax对输出归一化,放大较大的值,抑制较小的值。

10. 并行计算器件

略过。

posted @ 2023-02-17 17:38  白发戴花君莫笑  阅读(152)  评论(0编辑  收藏  举报