如何隐藏SHLD延迟?

时间:2012-08-17 18:39:45

标签: c++ optimization assembly x86 micro-optimization

我有一个简单的位读取器,它使用SHLD指令(__shiftleft128)来读取位流。

这很有效。但是,我一直在进行一些分析,我注意到SHLD指令后的任何指令都需要花费很多时间。

    Assembly                    CPU Time    Instructions Retired
add r10b, r9b                   19.000ms    92,000,000
cmp r10b, 0x40                  58.000ms    180,000,000
jb 0x140016fa6 <Block 24>       
        Block 23:       
and r10b, 0x3f                  43.000ms    204,000,000
mov r15, r11                    30.000ms    52,000,000
mov qword ptr [rbp+0x20], r11       
add rbx, 0x8                    16.000ms    78,000,000
mov qword ptr [rbp+0x10], rbx       
mov r11, qword ptr [rbx]        6.000ms     44,000,000
bswap r11                       2.000ms 
mov qword ptr [rbp+0x28], r11   8.000ms     20,000,000
        Block 24:       
mov rdx, r15                    61.000ms    208,000,000
movzx ecx, r10b                 1.000ms     6,000,000
**shld** rdx, r11, cl           24.000ms    58,000,000
inc edi                       **127.000ms** 470,000,000

正如您在上面的表格中看到inc指令占用大量时间(8%CPU时间)后的shld指令。

我想更多地了解为什么会出现这种情况以及如何避免这种情况?是否有任何指令可以与cpu级别的shld并行运行?

我记得在某些AMD优化手册中阅读shld,但我再也找不到了。

1 个答案:

答案 0 :(得分:1)

很难说,但似乎延迟是一些异常处理程序的结果。

<强>行为

然而,英特尔手册为shld指定了一些调用未定义响应的案例: -

  

目标操作数可以是寄存器或内存位置;该   源操作数是一个寄存器。计数操作数是无符号整数   可以存储在立即数字或CL寄存器中。如果   计数操作数是CL,移位计数是CL和a的逻辑AND   计数面具。在非64位模式和默认的64位模式下;只有0位   通过4计数使用。这会将计数屏蔽为值   在0到31之间。如果计数大于操作数大小,则为   结果未定义

如果计数为1或更大,则为CF.   标志填充最后一位移出目的地   操作数和SF,ZF和PF标志根据值设置   结果。对于1位移位,如果符号改变,则设置OF标志   发生了;否则,它被清除。对于大于1位的移位,   OF flag未定义。如果发生移位,则AF标志未定义。如果   计数操作数为0,标志不受影响。 如果计数是   大于操作数大小,标志未定义

shld的例外: -

In Protected Mode --> #GP(0),#SS(0),#PF(fault-code),#AC(0),#UD

UPDATE :: Gotcha: - &gt;
首先定义: -

  

已退役说明 - 事件选择C0H,Umask 00H
此事件计算退休时的指令数。有关说明   由多个微观操作组成,这个事件计算了退休   指令的最后一个微指令。带有REP前缀的指令   计为一条指令(不是每次迭代)。之前的错误   多声道指令的最后一个微操作的退出不是   计数。
此事件在VM退出条件下不会增加。   计数器在硬件中断,陷阱和中断期间继续计数   内部中断处理程序

inc edi **127.000ms** 470,000,000 (退出指令)
从上面的定义可以清楚地看出,这条指令会破坏太多的微操作,或者某些中断处理程序同时运行。