有没有另一种方法来计算 (a^3) * b + 5*(b^2) 没有那么多 mul 指令?
Is there another way to calculate (a^3) * b + 5*(b^2) without so many mul instructions?
我正在开始学习汇编,有人可以查看我的解决方案吗?
练习:
You are given eax=a, ebx=b.
Calculate (a^3) * b + 5*(b^2), and store
the result inside eax.
(Here * means multiplication, and c^d means c to the
power of d).
这是我的解决方案:
;Init the values
mov eax,3
mov ebx,2
;3,2 =>(3^3)*2 + 5*(2^2) = 27*2 + 5*4 = 54+20 = 74
;5,2 =>(5^3)*2 + 5*(2^2) = 125*2 + 5*4 = 250 + 20 = 270
;(a^3)*b
mov esi,eax
mul eax
mul esi
mul ebx
;Store the value
mov esi,eax
;5*(b^2)
mov eax,ebx
mul ebx
mov ebx,5
mul ebx
;Calculate (a^3)*b + 5*(b^2)
lea eax,[esi + eax]
有没有办法用更少的指令解决这个问题?
除非出于某种原因限制您使用所有指令,否则 imul
有 2 种和 3 种操作数形式可以更快地完成工作。您还可以使用分配规则来避免乘法:
mov ecx, eax ; c = a
imul ecx, eax ; c *= a (a*a)
imul ecx, eax ; c *= a (a*a*a)
lea eax, [ebx + ebx*4] ; a = (b + b*4) = 5*b
add eax, ecx ; a += c (5*b + a*a*a)
imul eax, ebx ; a *= b (b*[5*b + a*a*a])
忏悔:为了得到这个,我写了一个简单的 C 函数并将它编译成汇编。编译器甚至将分配规则应用于 b
。这段代码的质量说明了为什么编写汇编很少有回报。 (我知道现在大多数人都在学习汇编 阅读 出于网络安全目的,这很好。)
由于在 EDX:EAX 中不需要双宽度输出,而只是 32 位输入的 32 位结果,因此不要使用速度较慢且不太方便的 1 操作数 mul
或 imul
.
2 操作数 imul r, r
更快,因为它不必计算乘积的上半部分,也不必将其存储在任何地方。它适用于有符号和无符号数字。看起来您可以通过使用 2 操作数形式节省很多 mov
指令。
总结评论:
乘以小的常数因子是often best done with lea
。您最多可以用 lea
替换 4 条指令(包括 mov
),因为它为您提供了非破坏性操作。 (dest 不是来源之一)。
正如 spyr03 指出的那样,
a^3*b + 5*b^2 = b*(a^3 + 5*b)
您的最终指示:
lea eax,[esi + eax]
可能是一个简单的 add eax, esi
,它可以 运行 在比 lea
更多的执行端口上,因此它不太可能成为瓶颈的一部分。如果您不能用另一条指令执行等效操作,则仅使用 lea
。 (除了 imul
/ mul
。如果可能,始终用单个 lea
替换它们。对于前端吞吐量的延迟,有时值得使用最多两个 LEA 来替换一个 imul reg, reg, 37
或其他。)
所以我可能会这样做:
mov ecx, eax
imul ecx, eax ; ecx = a^2
imul eax, ecx ; eax = a^3
lea edx, [ebx + 4*ebx] ; edx = 5*b, ebx still = b
add eax, edx ; eax = a^3 + 5*b
imul eax, ebx ; eax = b * (a^3 + 5*b)
总是 注释您的 asm 代码。我喜欢这样的说法,asm 代码只能有两种错误:代码与注释不匹配,或者注释没有描述执行任务的有效算法。
延迟:
5*b
与 a^3
并行发生,并且速度更快(1 个周期)。 (我把它放在 a
的 imul
之后,以确保 CPU 尽快开始在更长的 dep 链上工作。)
长的 dep 链是涉及 eax
.
的那条
mov(1 或 0) -> imul(3) -> imul(3) -> add(1) -> imul(3)
总计 = 10 个周期
(在 IvyBridge 及更高版本上,reg-reg 移动在寄存器重命名阶段完成,并且延迟为零。)
虽然指令不是很多,而且它们都是单 uop 指令,因此有足够的空间让其他事情与它并行发生。
即使以更多指令为代价,我也没有看到任何缩短依赖链的余地。 a^3*b
和 5*b^2
可以并行计算,并在最后相加,但在两个链中较长的链中仍然是 3 次乘法。
我正在开始学习汇编,有人可以查看我的解决方案吗?
练习:
You are given eax=a, ebx=b.
Calculate (a^3) * b + 5*(b^2), and store the result inside eax.
(Here * means multiplication, and c^d means c to the power of d).
这是我的解决方案:
;Init the values
mov eax,3
mov ebx,2
;3,2 =>(3^3)*2 + 5*(2^2) = 27*2 + 5*4 = 54+20 = 74
;5,2 =>(5^3)*2 + 5*(2^2) = 125*2 + 5*4 = 250 + 20 = 270
;(a^3)*b
mov esi,eax
mul eax
mul esi
mul ebx
;Store the value
mov esi,eax
;5*(b^2)
mov eax,ebx
mul ebx
mov ebx,5
mul ebx
;Calculate (a^3)*b + 5*(b^2)
lea eax,[esi + eax]
有没有办法用更少的指令解决这个问题?
除非出于某种原因限制您使用所有指令,否则 imul
有 2 种和 3 种操作数形式可以更快地完成工作。您还可以使用分配规则来避免乘法:
mov ecx, eax ; c = a
imul ecx, eax ; c *= a (a*a)
imul ecx, eax ; c *= a (a*a*a)
lea eax, [ebx + ebx*4] ; a = (b + b*4) = 5*b
add eax, ecx ; a += c (5*b + a*a*a)
imul eax, ebx ; a *= b (b*[5*b + a*a*a])
忏悔:为了得到这个,我写了一个简单的 C 函数并将它编译成汇编。编译器甚至将分配规则应用于 b
。这段代码的质量说明了为什么编写汇编很少有回报。 (我知道现在大多数人都在学习汇编 阅读 出于网络安全目的,这很好。)
由于在 EDX:EAX 中不需要双宽度输出,而只是 32 位输入的 32 位结果,因此不要使用速度较慢且不太方便的 1 操作数 mul
或 imul
.
2 操作数 imul r, r
更快,因为它不必计算乘积的上半部分,也不必将其存储在任何地方。它适用于有符号和无符号数字。看起来您可以通过使用 2 操作数形式节省很多 mov
指令。
总结评论:
乘以小的常数因子是often best done with lea
。您最多可以用 lea
替换 4 条指令(包括 mov
),因为它为您提供了非破坏性操作。 (dest 不是来源之一)。
正如 spyr03 指出的那样,
a^3*b + 5*b^2 = b*(a^3 + 5*b)
您的最终指示:
lea eax,[esi + eax]
可能是一个简单的 add eax, esi
,它可以 运行 在比 lea
更多的执行端口上,因此它不太可能成为瓶颈的一部分。如果您不能用另一条指令执行等效操作,则仅使用 lea
。 (除了 imul
/ mul
。如果可能,始终用单个 lea
替换它们。对于前端吞吐量的延迟,有时值得使用最多两个 LEA 来替换一个 imul reg, reg, 37
或其他。)
所以我可能会这样做:
mov ecx, eax
imul ecx, eax ; ecx = a^2
imul eax, ecx ; eax = a^3
lea edx, [ebx + 4*ebx] ; edx = 5*b, ebx still = b
add eax, edx ; eax = a^3 + 5*b
imul eax, ebx ; eax = b * (a^3 + 5*b)
总是 注释您的 asm 代码。我喜欢这样的说法,asm 代码只能有两种错误:代码与注释不匹配,或者注释没有描述执行任务的有效算法。
延迟:
5*b
与a^3
并行发生,并且速度更快(1 个周期)。 (我把它放在a
的imul
之后,以确保 CPU 尽快开始在更长的 dep 链上工作。)长的 dep 链是涉及
的那条eax
.mov(1 或 0) -> imul(3) -> imul(3) -> add(1) -> imul(3) 总计 = 10 个周期
(在 IvyBridge 及更高版本上,reg-reg 移动在寄存器重命名阶段完成,并且延迟为零。)
虽然指令不是很多,而且它们都是单 uop 指令,因此有足够的空间让其他事情与它并行发生。
即使以更多指令为代价,我也没有看到任何缩短依赖链的余地。 a^3*b
和 5*b^2
可以并行计算,并在最后相加,但在两个链中较长的链中仍然是 3 次乘法。