1

我有一个简单的位读取器,它使用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正如您在指令上表中所见,该shld指令占用了大量时间(8% CPU 时间)。

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

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

4

1 回答 1

1

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

行为

但是,英特尔手册指定了shld调用未定义响应的几种情况:-

目的操作数可以是寄存器或内存位置;源操作数是一个寄存器。计数操作数是一个无符号整数,可以存储在立即字节或 CL 寄存器中。如果计数操作数是 CL,则移位计数是 CL 和计数掩码的逻辑与。在非 64 位模式和默认 64 位模式下;仅使用计数的 0 到 4 位。这会将计数屏蔽为 0 到 31 之间的值。如果计数大于操作数大小,则结果为 undefined

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

shld 例外:-

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

UPDATE:: Gotcha:-->
首先定义:-

Instructions Retired — 事件选择 C0H,Umask 00H
此事件计算退出时的指令数。对于由多个微操作组成的指令,此事件计算指令的最后一个微操作的退出。带有 REP 前缀的指令算作一条指令(不是每次迭代)。多操作指令的最后一个微操作退出之前的故障不计算在内。
此事件在 VM 退出条件下不会增加。 计数器在硬件中断、陷阱和内部中断处理程序期间继续计数

inc edi **127.000ms** 470,000,000(指令退出)
从上面的定义中可以清楚地看出,要么这条指令进入了太多的微操作,要么同时运行了一些中断处理程序。

于 2012-08-17T19:08:31.410 回答