我想知道是否有一种无需使用MUL或DIV指令即可执行任何乘法或除法的方法,因为它们需要大量的CPU周期。我可以针对该目标利用SHL或SHR指令吗?如何实现汇编代码?
我需要有关特定数字的帮助-如何仅用5条命令将bx
乘以41?
每当我尝试解决问题时,我至少会得到6条命令...
我的代码:
mov ax,bx
mov cx,bx
shl bx,5 ; *32
shl ax,3 ; *8
add bx,ax ; *40
add bx,cx ; *41
答案 0 :(得分:4)
; ax = x
mov bx, ax ; bx = x
shl bx, 3 ; bx = 8 * x
add ax, bx ; ax = 9 * x
shl bx, 2 ; bx = 32 * x
add ax, bx ; ax = 41 * x
答案 1 :(得分:3)
您要调整哪些CPU?您真的是说实际的8086吗?它们仍然以微控制器的形式存在,但是如今大多数x86代码都在现代x86上运行。
现代的x86 CPU具有非常快的乘法器,因此通常只有在您可以2或更少的时间内完成工作时才需要使用shift / add或LEA。 div
/ idiv
仍然很慢,但是乘积不足以解决问题的现代CPU却没有乘法。
imul eax, ebx, 41
在现代Intel CPU和Ryzen(https://agner.org/optimize/)上具有3个周期延迟,每个时钟吞吐量1个时钟延迟,并且在286及更高版本上受支持。 (16位格式imul ax, bx, 41
是2微秒而不是1微秒,在Sandybridge系列CPU上有4个周期的延迟。)
如果您可以使用32位寻址模式(386和更高版本),则可以按照2条LEA指令进行操作(在现代CPU上总共为2 uops,有2个周期的延迟)。 / p>
看看gcc / clang如何编译此函数(on the Godbolt compiler explorer):
int times41(int x) { return x*41; }
# compiled for 32-bit with gcc -O3 -m32 -mregparm=1
times41(int): # first arg in EAX
lea edx, [eax+eax*4] # edx = eax*5
lea eax, [eax+edx*8] # eax = eax + edx*8 = x + x*40
ret
对于在imul
或mul
上使用oups的较旧的CPU而言,这是最好的选择,并且对于现代CPU而言,延迟比uop计数更重要。
在您的16位代码中,您可以使用
lea eax, [ebx+ebx*4] # ax = bx*5
lea ax, [ebx+eax*8] # ax = bx + ax*8 = x + x*40
为第一个LEA使用32位操作数大小可以避免对EAX的旧值的错误依赖,并避免在Nehalem和更早版本上发生部分寄存器停顿(从第二个LEA在写入AX之后读取EAX)。
对于操作数大小的前缀(以及地址大小的前缀),它仅花费1个额外的代码大小字节,并且对正确性没有影响。 (左移和加法结果的低16位不取决于输入的高位。)
或者您可能想在编写AX之前xor eax,eax
,以使Intel CPU避免部分寄存器合并以供将来使用AX。 (Why doesn't GCC use partial registers?)。