3.3 调度
调度或配对是各代处理器都应使用的一种优化性能的方法。下面是一个在奔腾和P6系列处理器上,用以提高代码速度的配对和调度表。在某些情况下,对特定的处理器具有达到最佳性能的变通方案。这些变通的方法因应用程序的特性而不同。在超标量的奔腾处理器中,指令的顺序是使处理器达到最高性能的非常重要的因素。
对指令顺序的重新安排增加了将两条指令安排成同时运行的可能性。与数据相关的指令应被至少一条其它指令所分开。
本书描述了你需要了解的MMX™指令与整数指令配对的原则。对下表列出的每一种情况,都在小节中说明了与之相配的原则。
进行配对所需了解的几个规则:
通用配对规则:规则仅与机器状态有关,与指定的操作码无关。对整数和浮点同样有效。例如,单步指令禁止指令配对。
整数配对规则:对整数指令配对的规则。
对MMX™指令配对的规则:由于只存在一个乘法器单元,规则允许两个MMX™指令同时执行。
MMX™指令与整数指令配对规则:对一条MMX™指令和一条整数指令配对的规则。
注意 浮点指令不可以与MMX™指令配对。
3.3.1 通用配对规则
关于奔腾处理器通用配对规则,请参见《对Intel32位处理器的优化》应用部分 AP—526(序号242816),具有MMX™技术的奔腾处理器降低了一些通用配对的规则要求:
如果两条指令中存在一条长度大于7字节的指令,奔腾处理器不对它们进行配对。具有MMX™技术的奔腾处理器,不对第一条指令大于11字节或第二条指令大于7字节的指令进行配对,前缀不计算在内。
有前缀的指令可在U管道配对,前缀为0FH,66H或67H的指令也可在V管道中配对。
3.3.2 整数配对规则
当下列两种情况发生时不能进行配对:
后两条指令是不可配对指令(见下表可配对指令汇总:并参见《对Intel32位处理器的优化》应用部分AP-526,附录A包含了一个指令配对特性的完整列表)。通常,大多数简单的ALU指令是可配对的。
后两条指令属于寄存器争用(显式或隐式的)的一些类型。在较少的情况下,寄存器争用的指令也可配对。这种情况在3.3.2.3中解释。
表3-1 整数指令配对
在 U 管道中可配对指令 在 V 管道中可配对指令
mov r,r alu r,i push r mov r,r alu r,i push r
mov r,m alu m,i push i mov r,m alu m,j push l
mov m,r alu eax,i pop r mov m,r alu eax,i pop r
mov r,i alu m,r nop mov r,i alu m,r jmp near
mov m,i alu r,m shift/rot by l mov m,i alu r,m jcc near
mov eax,m inc/dec r shift by imm mov eax,m inc/dec r 0F jcc
mov m,eax inc/dec m test reg,r/m mov m,eax inc/dec m call near
alu r,r lea r,m test acc,imm alu r,r lea r,m nop
test reg,r/m test acc,imm
3.3.2.1 配对指令集
不可配对指令(NP)
移位计数在cl的shift/rotate指令。
长算术指令,如:MUL,DIV。
扩充指令,如:RET,ENTER,PUSHA,MOVS,STOS,LOOPNZ。
某些浮点指令,如FSCALE,FLDCW,FST。
跨段(Inter-Segment)指令,如:PUSH sreg,CALL far。
在 U或 V管道中的可配对指令(UV)
大多数8/32位ALU操作,如:ADD,INC,XOR。
所有的8/32位比较指令,如:CMP,TEST。
所有8/32位对寄存器操作的指令,如:PUSH reg,POP reg。
U管道中可配对指令(PU)
下列指令必须在U管道中使用,并可与适当的V管道中指令配对,这些指令不能在V管道中执行。
进位和借位指令,如:ADC,SBB。
有前缀的指令,但0FH,66H或67H前缀的指令除外(参见3.2.3节)。
用立即数移位的指令。
某些浮点操作,如:FADD,FMUL,FLD。
在V管道配对的指令(PV)
这些指令可以在U管道或V管道中执行,但只能在V管道中配对。由于这些指令改变了指令指针(eip),所以如果下一个指令可能是不相邻的指令时,不能在U管道中配对。即使当一个管道中的分支被预测为“未提取”时,当前指令也不能和下一指令配对。
简单的控制传输指令,如:Call near,jmp near,jcc。包括jcc的短分支和jcc近分支(具有0F前缀)的条件分支指令。
fxch
3.3.2.2 由于寄存器相关而不能配对的指令
指令配对也受指令操作数的影响,由于寄存器争用使下列组合不能配对,本规则的例外情况见下节。
第一条指令向一个寄存器写,而第二条指令从该寄存器中读(流相关)。例如:
mov eax, 8
mov [ebp], eax
两条指令均写向同一寄存器(输出相关),如下例:
mov eax, 8
mov eax, [ebp]
对EFLAGS寄存器写的指令配对不受此限(例如,两个变换状态编码的ALU操作),配对后,指令执行的状态编码为V管道指令的状态。
注意,两条指令中,如果第一条读一个寄存器,且第二条写一个已知状态(反相关),则可以配对,见下例:
mov eax, ebx
mov ebx, [ebp]
为了确定寄存器争用,对一个字节或一个字寄存器的引用视同对一个完整32位寄存器的引用。因此,
mov al, 1
mov ah, 0
由于在EAX寄存器内容上输出相关,故不能配对。
3.3.2.3 特定配对
某些特定指令不受上述“通用”规则限制。这些特定配对不受寄存器相关限制,且大多数隐式地读/写esp寄存器或隐式地写状态码。
堆栈指针:
push reg/imm ; push reg/imm
push reg/imm ; call
pop reg ; pop reg
状态代码:
cmp ; jcc
add ; jne
注意这些特定配对由PUSH/POP指令指定,且只能是立即数或寄存器操作数,不能是内存操作数。
3.3.2.4 配对执行的限制
有些配对指令可以同时安排进入管道,但却并不能并行执行。作为对U管道MMX™指令与V管道整数指令配对规则的补充,须增加下列两条规则。
如果两条指令都访问同一个数据高速缓存的存贮体,则第二条(V管道)指令的申请必须等到第一条指令的申请完成。当两个物理地址的第2至4位相同时,发生存贮体冲突。存贮体冲突的产生将对V管道的指令产生一个时钟周期的额外迟延。
跨管道的并发执行将保持内存访问的顺序。一条U管道中的多时钟周期的指令将单独执行,直到它最后的内存数据存取完成。
add eax, meml
add ebx, mem2 ; 1
(add) (add) ; 2 2-周期
上述指令将寄存器内容和内存单元的数值相加,然后将结果置于寄存器中,带内存操作数的加法将耗费两个时钟周期。第一时钟周期从高速缓存中取数据,第二时钟周期执行加法,由于只有U管道指令有一个内存访问,V管道的加法可以在同一时钟周期启动。
add mem1, eax ; 1
(add) ; 2
(add)add mem2, ebx ; 3
(add) ; 4
(add) ; 5
上述指令将寄存器内容与内存单元的数值相加,然后将结果置于内存单元中。结果放在内存的加法,执行时耗费三个时钟周期。第一个时钟周期取值,第二个时钟周期执行加法,第三个时钟周期存贮结果,在配对时,U管道指令的最后一时钟周期与V管道的第一时钟周期在执行时重叠。
在已执行的指令未运行完时,其它指令不可运行。 为了最佳地把握调度和配对的机会,一种较好的作法是:在耗费时钟周期数相等的情况下,尽量安排简单指令序列代替复杂指令。简单指令在组织时间片上占有优势。load/store类型的代码类要求更多寄存器并将增大代码大小。为满足这种对额外寄存器的需求,应在寄存器分配和指令调度上进一步努力,使仅当并行性扩大时,额外的寄存器可被使用。
3.3.3 MMX™指令配对准则
本节说明了MMX™指令之间的配对准则和MMX™与整数指令的配对准则。
3.3.3.1 两个MMX™指令的配对
由于只存在一个MMX™移位器单元,两条都使用MMX™移位器单元(pack,unpack和shift类指令)的MMX™指令不能配对。移位操作既可以安排在U管道执行,也可以安排在V管道执行,但不能在同一个时钟周期内安排在两个管道内执行。
由于只存在一个MMX™乘法器单元,所以两条都使用MMX™乘法器单元(pmull,pmulh,pmadd类指令)的MMX™指令不能配对。乘法操作可以安排在U管道,也可以安排在V管道,但不能在同一时钟周期内安排在两个管道内执行。
对内存或整数寄存器文件访问的MMX™指令只能安排在U管道中。不要将这些指令调度到V管道中。应使它们等待,并安排在下一个指令对中(且位于U管道中)。
U管道指令的MMX™目的寄存器不能与V管道指令的源或目的寄存器匹配(相关检查)。
EMMS指令不可配对。
如果CR0.TS或CR0被设置,MMX™指令不能进入V管道。
3.3.3.2 U管道的整数指令与V管道中的MMX™指令配对
浮点指令不能紧跟MMX™指令。
V管道的MMX™指令不能访问内存或整数寄存器文件。
U管道的整数指令是可配对的U管道整数指令(参见表3-1)。
3.3.3.3 U管道的MMX™指令与V管道中的整数指令配对
V管道指令是一个可配对的整数V管道指令(参见表3-1)。
U管道的MMX™指令不能访问内存或整数寄存器文件。
3.3.3.4 调度规则
包括乘法指令在内,全部MMX™指令均可流水作业,除乘法指令耗费3个时钟周期外,全部指令执行时耗费一个时钟周期。
由于乘法指令执行时用三个时钟周期,所以乘法指令后的结果只能被安排在三个时钟周期后的指令使用。考虑这一因素,避免在乘法指令后的两个指令对内调度一条与之相关的指令。
如2.1.1节所述,在对寄存器写后再对寄存器存贮,必须等待两个时钟周期来修改寄存器。为避免管道阻塞,存贮调度在修改寄存器的两个时钟周期后进行。