Intel, AMD及VIA CPU的微架构(34)

16. VIA Nano流水线

尽管它们小的低功耗设计,VIA Nano处理器有一个完全乱序的微架构,功能与性能与更耗能的Intel与AMD桌面处理器相差不远。虽然它有几个弱点,性能不好。现在可用的版本有一、二或四个核,每个运行1个线程。Nano 2000系列支持补充SSE3与x64指令集。Nano 3000系列还支持SSE4.1指令集与虚拟指令。这个微架构由G. Glenn Henry在:"The VIA Isaiah Architecture", Centaur Technology, 2008 (www.via.com.tw)里描述。

16.1. 性能监控计数器

在当前手册中描述的、我在其他处理器上的研究,严重依赖性能监控计数器。Nano也有性能监控计数器,但这些完全没有文档,仅为了内部使用。我发现了几个用于分支预测、μop等的计数器,但这些计数器有些不可靠,特别在2000系列上。因此,下面展示的发现主要基于时钟计数的测量。我发现的计数器列在www.agner.org/optimize的我的测试程序源代码里。

16.2. 指令获取

最大指令获取速度是每时钟周期16字节,在大多数情形里这可能是足够的。

16.3. ​​​​​​​​​​​​​​指令解码

解码器每时钟周期可以处理3条指令,包括产生多个μop的指令。带有任意数目前缀的指令可以无时延解码。对长度改变前缀没有惩罚。

​​​​​​​16.4. 指令融合

解码器可以将一条整数ALU指令与一条分支指令融合为一个μop。仅在满足以下条件时,指令融合奏效:

  • 第一条指令是以下之一:CMP,ADD,SUB,INC,DEC,TEST,AND,OR,XOR。
  • 第一条指令有1或2个寄存器操作数,没有立即数,没有内存操作数。
  • 在Nano 2000系列上,第二条指令仅可以是JE或JNE(与JZ及JNZ相同)。
  • 在Nano 3000系列上,第二条指令可以是任何条件跳转。一个读进位标记的条件跳转(即无符号比较,如JA,JB)不能与一条不使用进位标记的ALU指令融合。
  • 之间不能有其他指令(在3000上除了NOP)。

例如,对组合DEC ECX / JNZ LOOP1,指令融合可以工作,但对CMP ECX, 1000 / JNZ LOOP2不能,因为有一个立即数。

在Nano 3000系列上,NOP可与之前一条指令融合,因此它不使用执行单元的任何资源。NOP可与许多常见指令融合,包括SIMD指令,但不是所有指令。这适用于单字节NOP(opcode 90)以及多字节NOP(opcode 0F 1F xxx),但不适用于FNOP以及其他用作NOP的指令(如LEA,MOV)。

​​​​​​​16.5. 乱序系统

寄存器重命名、保留站与μop分发工作良好,吞吐率是每时钟周期3条指令。

​​​​​​​16.6. 执行端口

有7组执行单元,每个有自己的执行端口。它们被命名如下:

Port

Nano 2000

Nano 3000

I1

整数加法、布尔、偏移、旋转

整数加法、布尔、偏移、旋转、移动

I2

整数加法、布尔、移动、跳转

整数加法、布尔、移动、偏移、旋转、乘法(32位)、跳转

MA

所有操作数类型上的乘法、除法、平方根

浮点、SIMD、64位整数乘法。除法与平方根

MB

所有浮点、SIMD操作数上的其他操作

所有浮点、SIMD操作数上的其他操作

SA

内存写与LEA的地址计算

内存写与LEA的地址计算

ST

内存写

内存写

LD

内存读

内存读

表16.1. VIA Nano的执行单元

每个执行端口每时钟周期可以处理一个μop。对大多数整数操作,时延是1时钟周期。32位整数乘法,在3000系列上是2时钟周期,在2000系列上是4 ~ 6时钟周期。浮点加法需要2时钟周期。浮点乘法需要3 ~ 4时钟周期。这些操作流水线化为每时钟周期一个操作的吞吐率。双精度浮点乘法有一半吞吐率。除法没有流水线化。

内存写指令使用SA与ST端口,显然使用一个去往这两个端口的融合μop。LEA指令使用SA端口。内存读指令仅使用LD端口。读-修改指令产生两个μop,一个用于LD端口,一个用于执行端口。对每个端口,最大吞吐率是每时钟周期1个μop。

手册4“指令表”列出了每条指令的时延与使用的端口。表项不包括下面描述的额外时延。

对在同一个执行端口混用不同时延的μop没有惩罚。在代码主要包含端口I1与I2、修改通用寄存器的指令时,效率低下。当这两个端口的队列填满时,调度器将所有后续I1/I2指令提交到队列I1。这导致端口I2次优的使用。可以通过确保没有超过2/3的指令使用端口I1与I2,避免这。在Nano 2000系列上,NOP指令是古怪的。看起来阻塞了所有的执行端口,使其他指令不能并行执行。这仅适用于单字节NOP指令(opcode 90)。多字节NOP(opcode 0F 1F xxx)以及FNOP没有这个问题。Nano 3000系列没有这个问题。

​​​​​​​16.7. 执行单元间的时延

当在一个不同执行单元或子单元中的后续一条指令需要一条指令的输出时,通常有一个额外的时延。从一个单元移动数据到另一个测量所得时延,在表16.2中列出。

 

至端口

自端口

I1 / I2

MA

MB

MB-fadd

LD, ST

SA

I1 / I2

0

1-2

 

 

0

0

MA

1

0

1

1

0

0-1

MB

 

0-1

0

0-1

1-2

0-1

MB-fadd

 

0-1

0-1

0

1

0-1

LD, ST

0

0-1

1

1

0

0

SA

2

1-2

 

 

0

0

表16.2. 执行单元间的时延

在表16.2中缺少的项是不可能的操作或不能测量。在手册4“指令表”中列出的时延没有包括表16.2中的额外时延,除了在XMM与通用寄存器间移动数据的指令。在Nano 3000系列中单元间的时延有时比2000系列要高。

MB下的浮点加法器在这里称为MB-fadd。注意在使用XMM寄存器时,MB下的浮点加法器与MB下的其他单元间有额外时延,但在使用x86寄存器时没有。

以下例子展示了单元间的时延。

; Example 16.1a. Latencies between units

; Unit Latency

mov rax, [m1]                                                     ; LD 2

add rax, 2                                                            ; I12 1

imul rax, 10                                                         ; MA 3+2

add rax, rbx                                                         ; I12 1+1

mov [m2], rax                                                     ; ST 2

                                                                              ; Total: 12 expected, 12-13 measured

 

; Example 16.1b. Instructions reordered

                                                                               ; Unit Latency

mov rax, [m1]                                                      ; LD 2

imul rax, 10                                                          ; MA 3

add rax, 2*10                                                       ; I12 1+1

add rax, rbx                                                          ; I12 1

mov [m2], rax                                                       ; ST 2

                                                                                ; Total: 10 expected, 10-11 measured

 

; Example 16.1c. Using I12 unit only

                                                                                ; Unit Latency

mov rax, [m1]                                                       ; LD 2

add rax, 2                                                               ; I12 1

mov rcx, rax                                                           ; I2 (1)

shl rax, 3                                                                 ; I1 1

add rcx, rcx                                                             ; I12 (1)

add rax, rbx                                                            ; I12 1

add rax, rcx                                                             ; I12 1

mov [m2], rax                                                         ; ST 2

                                                                                  ; Total: 8 expected, 9 measured

 

; Example 16.1d. Using SA unit only

                                                                                  ; Unit Latency

mov rax, [m1]                                                         ; LD 2

lea rax, [rax+4*rax+10]                                         ; SA 1

lea rax, [rbx+2*rax]                                                ; SA 1

mov [m2], rax ; ST 2

                                                                                  ; Total: 6 expected, 6 measured

例子16.1 a-d都做相同的事情。在16.1a里,我们浪费了3时钟周期在I12与MA间来回移动数据上。在16.1b中,我们通过重排指令避免了一个时延。在16.1c中,我们使用偏移替代乘法,避免了到MA单元的迁移。在16.1d中,我们对乘法与加法使用了LEA。通过将所有计算保持在SA单元,避免了时延。如果混合了LEA与ADD指令(分别是SA与I1/I2单元),将有额外的2时钟周期的时延。

这些传输时延,在以下情形里,可能出现在依赖链中:

  • 通用寄存器中,混合了乘法(MA),除法(MA)或LEA指令(SA)与其他整数指令(I1/I2)。(在3000系列上,MA用于64位乘法,但不用于32位或更小的乘法)。
  • 在XMM寄存器中,整数指令将乘法(MA)与其他指令(MB)混用。
  • 在XMM寄存器中,浮点指令将乘法、除法或平方根(MA),加法(MB-fadd)与其他指令(MB)混用。
  • 在x87寄存器中,浮点指令将乘法、除法或平方根(MA)与加法及其他指令(MB)混用

这些单元间的迁移数应该尽量少。在读、写内存以及在通用寄存器与SIMD寄存器间传输数据时,不同单元间的迁移不可避免。

整数与浮点类型XMM指令间的时延

在打算用于整数数据的XMM指令与打算用于浮点数据的XMM指令混用时,有可观的时延。一些XMM指令有3个不同版本,一个用于整数数据,一个用于单精度,一个用于双精度。例如,MOVDQA,MOVAPS,MOVAPD与POR,ORPS,ORPD。在Nano处理器上,混用不同类型的指令,有严厉的惩罚。对从一条整数类型指令到一条浮点类型指令的转换,有最多2时钟周期的惩罚,而对从一条浮点类型指令到一条整数类型指令的转换,有3时钟周期。最可能的解释是,整数与浮点XMM指令使用不同的执行单元,不同的寄存器或不同的数据总线。

使用错误类型的指令,通过发生在以下情形中:

  • 指令的单精度版本比整数或双精度版本要短一字节。例如,某些编译器为了使代码更短,使用MOVAPS,而不是MOVDQA。这应该避免,因为在Nano上指令获取很少是瓶颈。
  • 某些整数XMM指令没有浮点对等物,例如偏移指令与PSHUFD。在一个浮点上下文里使用这些指令的惩罚通常是3时钟周期。应该尽可能使用SHUFPS或SHUFPD。
  • 某些浮点指令没有整数对等物,例如MOVSS,MOVSD与SHUFPS。在一个整数上下文里使用这些指令的惩罚通常是5时钟周期。

这些惩罚与寄存器里实际包含何种类型数据无关,仅与不同类型指令间的转换有关。例如,使用MOVAPS移动数据没有惩罚,只要不在这些数据上进行计算。

​​​​​​​16.8. 部分寄存器与部分标记

在修改通用寄存器或XMM寄存器的一部分时,没有部分寄存器暂停。寄存器总是作为整体处理,这会导致假的依赖。例如,到AX的移动必须等待之前所有的EAX写完成,因为不能将AX独立出来作为无关的寄存器。由MOVZX EAX, word ptr [mem]替换MOV AX, [mem]来避免这个。不需要把写扩展到RAX,因为对EAX的写将清零RAX的高半部。

标记寄存器以相反的方式处理。标记寄存器可以分解为部分,以避免对修改部分标记指令的假的依赖(如INC)。结果,在部分标记寄存器必须合并起来的情形里,有一个7时钟周期的暂停。例如:

; Example 16.2. Partial flags stall

add eax, 2          ; modifies all flags

inc ebx                ; modifies all flags except carry

setbe cl               ; needs carry flag from add and zero flag from inc

在PUSHF指令中,部分标记寄存器暂停是不可避免的。

​​​​​​​16.9. 打破依赖

处理器知道在与自身XOR时,一个寄存器被清零,例如XOR EAX, EAX被视为与EAX之前值无关。这同样适用于以下指令:

Instruction

Nano 2000

Nano 3000

XOR

x

x

SUB

-

x

SBB

-

-

PXOR, XORPS, XORPD

x

x

PANDN, ANDNPS, ANDNPD

-

-

PSUBB/W/D/Q

-

x

PCMPEQW/D

-

x

PCMPEQQ

-

-

PCMGTW/D/Q

-

-

​​​​​​​16.10. 内存访问

缓存:

  • 1级指令缓存。64kB,16路,组相联,每行64字节
  • 1级数据缓存。64kB,16路,组相联,每行64字节
  • 2级缓存。1 MB,16路,组相联,每行64字节

如果在Nano 2000系列上跨32字节边界,或在3000系列上跨64字节边界,非对齐内存访问代价很高。对非对齐读,时延大约是18个时钟周期,对非对齐写大约是37个时钟周期。如果没有跨32/64字节边界,没有惩罚。

写转发仅在简单情形里有效工作:

读大小

地址偏移

Nano 2000额外时延

Nano 3000额外时延

= 写大小

0

0

0

< 写大小

0

0

0

> 写大小

0

11

9

< 写大小

1

33

7

< 写大小

2-4

7

7

< 写大小

> 4

33

7

任何大小

仅部分重叠

21-33

18

​​​​​​​16.11. 分支与循环

跳转、调用与被采用分支的吞吐率是每3时钟周期一个跳转。不采用分支有每时钟周期一个的吞吐率。如果循环没有包含16字节边界,该循环的最小执行时间是3时钟周期,如果跨了一个16字节边界,是4时钟周期。因此,紧凑循环应该对齐在16字节边界,保持时钟周期为3。

在每16字节的代码行中,分支目标缓冲仅可以记录两个跳转。如果一个对齐的16字节代码块包含超过2个被采用的跳转、调用或返回,那么超出的跳转,每个有8时钟周期的时延。

如果每个16字节代码块中没有超过1个分支或跳转,分支预测工作得最好,不过在16字节代码块有2个分支时,预测也相当好。细节参考第23页。返回的预测良好。

​​​​​​​16.12. VIA特殊指令

处理器对加密、解密及哈希有特殊指令。这些复杂指令以每字节数据1 ~ 4时钟周期的速度运行。

还有一条基于电子噪声的随机数生成指令。依赖于要求的质量与处理器版本,这条指令需要1300到19200个时钟周期来产生8字节随机比特。随机比特被持续取样到一个8字节缓冲,同时处理器进行其他任务。仅当缓冲满时,指令XSTORE得到8个随机字节,而指令REP XSTORE则等到取样到足够的随机数据。

对Monte Carlo应用,产生长的随机数序列,这条指令太慢,但为一个伪随机数生成器或一个加密密钥生成种子,是足够的。

通过DIEHARD序列测试(en.wikipedia.org/wiki/Diehard_tests),我测试了生成的随机数质量。在质量因子设为最低值时,由REP XSTORE指令产生的随机数在许多测试中失败了。这是预期的,因为物理生成器的随机性已知是不完善的。使用更高的质量因子,它通过了所有的测试。通过更长时间间隔取样噪声,以及使用一个未公开算法的“漂白者(whitener)”处理随机数据,获得更高的质量。

这些指令的编程指引可以在:: "VIA PadLock Programming Guide", www.via.com.tw, May 2005,找到。

​​​​​​​16.13. Nano里的瓶颈

Nano中的执行单元,对这样小的一个处理器来说,相当强大,执行时延非常低。但是,有若干弱点:

  • 非对齐内存访问代价很高。
  • 写转发仅在相对简单的情形里奏效。
  • 数据缓存访问限制为每时钟周期一次读或一次写。极少能同时读写。
  • 分支密度受限。有每16字节代码两个跳转或分支的限制。在超过这个限制时,性能下降。
  • 分支吞吐率。Nano每3个时钟周期不能进行超过一个被采用的分支或跳转。
  • 在大多数指令去往I1与I2执行端口时,有次优的排队。
  • 在不同执行单元或子单元间移动数据,有许多额外时延。

Nano 2000与3000系列间主要的区别在于执行单元。Nano 3000对大多数整数指令有两个执行单元。它有一个2时钟周期时延的新整数乘法单元。改进了除法,SIMD指令有几个小改进。非对齐内存访问与写转发有些提高,但仍然不如更大的处理器高效。

结论是,对大多数苛刻的、内存密集的应用程序,单核Nano可能不能胜任。低价格与低功耗使它对许多常见的应用,如办公应用、嵌入式应用以及低流量服务器是有价值的。

猜你喜欢

转载自blog.csdn.net/wuhui_gdnt/article/details/87875493