返回顶部
首页 > 资讯 > 操作系统 >5.7 汇编语言:汇编高效乘法运算
  • 851
分享到

5.7 汇编语言:汇编高效乘法运算

汇编汇编语言Windows汇编x86 2023-08-30 10:08:47 851人浏览 薄情痞子
摘要

乘法指令是一种在CPU中实现的基本算术操作,用于计算两个数的乘积。在汇编语言中,乘法指令通常是通过mul(无符号乘法)和imul(有符号乘法)这两个指令实现的。由于乘法指令在执行时所消耗的时钟周期较多,所以编译器在优化代码时通常会尝试将乘法

乘法指令是一种在CPU中实现的基本算术操作,用于计算两个数的乘积。在汇编语言中,乘法指令通常是通过mul(无符号乘法)imul(有符号乘法)这两个指令实现的。由于乘法指令在执行时所消耗的时钟周期较多,所以编译器在优化代码时通常会尝试将乘法操作转换为更高效的加法、和移位操作。

  • 对于较小的数,编译器可能会选择将乘法操作直接转换为加法操作。例如,将表达式a * b转换为a + a + ... + a(b次相加)的形式。这种方式可以通过循环展开、代码向量化等技术来优化。

  • 对于较大的数,编译器可能会使用位移和移位操作来代替乘法。例如,将表达式a * b转换为a << n + a << m的形式,其中nm为符合条件的位数。这种方式可以通过位移指令的高效性来加速运算。

当以上方式均无法进行优化时,编译器才会使用mul/imul指令来执行乘法操作。这两条指令可以对无符号数和有符号数进行乘法运算,即便这两条指令会使用更多的时钟周期,但乘法指令的计算效率相对于其他指令DIV来说仍然较低,因此在编写高效代码时,应尽可能地避免使用乘法操作,并结合使用上面提到的技巧进行优化。

7.1 使用IMUL指令完成乘法

要计算乘法在不考虑执行效率的情况下编译器通常会直接使用imul指令完成计算,imul指令在一些情况下可以比其他乘法指令(如mul指令)更快地执行乘法运算,但性能较低的原因主要是由于imul指令通常用于有符号数的乘法运算,并且在执行时需要处理符号位的扩展和溢出问题,这转换成了额外的指令和时钟周期的消耗。如果对于无符号整数或需要使用寄存器的低位或者高位结果的情况,使用imul指令可以提供一定的优势。

计算乘法时应遵循:

  • 如果乘数与被乘数都是8位 则把AL做乘数,结果放在AX
  • 如果乘数与被乘数都是16位 将把AX做乘数,结果放在EAX
  • 如果乘数与被乘数都是32位 将把EAX做乘数,结果放在EDX:EAX

乘法指令计算很简单,只需要累加乘数即可,如下所示则是一个简单的计算三个数相乘的汇编实现;

.data    x DWord ?    y DWORD ?    z DWORD ?    szFmt BYTE '计算结果: %d',0dh,0ah,0.code    main PROC      mov dword ptr ds:[x],10      mov dword ptr ds:[y],24      mov dword ptr ds:[z],18            ; 计算 x * y * z      mov eax,dword ptr ds:[x]      imul eax,dword ptr ds:[y]      imul eax,dword ptr ds:[z]      invoke crt_printf,addr szFmt,eax    main ENDPEND main

7.2 使用LEA指令替换乘法

在实际编程中,我们可以使用LEA指令来替代乘法操作,从而提高代码的执行效率。但读者需要注意,在使用LEA计算乘法时必须要保证乘数是2的次幂,并且乘数的范围必须是2/4/8这三个区间才可使用该指令,我们使用汇编来实现计算eax*8+2其汇编指令如下。

  • 假设 eax=5 计算 eax * 8 + 2 的结果,拆分过程如下:
  • 1.计算 lea ebx,dword ptr ds:[eax * 8 + 2] 这就相当于计算 ebx = (eax * 8) +2直接可得到结果。

第一个案例比较简单,可直接使用一条lea指令即可完成计算过程,只要保证被乘数是2的次幂即可。

.data  x DWORD ?  szFmt BYTE '计算结果: %d',0dh,0ah,0.code  main PROC    ; 针对乘法的lea指令优化    mov dword ptr ds:[x],5        mov eax,dword ptr ds:[x]               ; eax = x    xor ebx,ebx; ebx = 0    lea ebx,dword ptr ds:[eax * 8 + 2]     ; ebx = eax * 8 + 2    invoke crt_printf,addr szFmt,ebx        invoke ExitProcess,0  main ENDPEND main

7.3 使用LEA指令拆分计算

如果我们计算的乘法超出了2/4/8次幂范围,则需要对乘法进行拆分,拆分时也应遵循2的次幂原则,拆分后在分开来计算。

  • 假设 eax=3 计算 15 * eax 的结果,拆分过程如下:
  • 1.计算 lea edx,[eax * 4 + eax] 这就相当于计算 edx = (4 * eax) + eax = 5eax 其中的每个edx就相当于5个eax
  • 2.计算 lea edx,[edx * 2 + edx] 这就相当于计算 edx = (5 * eax) * 2 + (5 * eax)
  • 3.计算 (5eax * 2) = 10eax 接着计算 (5 * eax) = 5eax 最后得出 10eax + 5eax
  • 4.经过该过程可得出 eax * 15 = 45 最终计算3*15=45得到最终结果.

这个计算过程看似复杂,但如果将其转化为汇编指令那么只需要两条即可实现快速乘法运算。

.data  x DWORD ?  szFmt BYTE '计算结果: %d',0dh,0ah,0.code  main PROC    ; 针对乘法的lea指令优化    mov dword ptr ds:[x],3        ; 如果使用lea计算乘法,则乘数必须是2/4/8    mov eax,dword ptr ds:[x]               ; eax = 3    lea edx,dword ptr ds:[eax * 4 + eax]   ; edx = 4eax + eax 得出 5eax,也就是说每一个edx就代表5个eax    lea edx,dword ptr ds:[edx * 2 + edx]   ; edx = (5eax * 2) + 5eax 最终得出 15eax    invoke crt_printf,addr szFmt,edx       ; edx = eax * 15 计算后得出 45        invoke ExitProcess,0  main ENDPEND main

7.4 使用LEA指令递减计算

如果计算乘法时乘数非2的次幂,这种情况下需要减去特定的值,例如当我们计算eax * 7时,由于7非二的次幂,我们无法通过lea指令进行计算,但我们可以计算eax * 8计算出的结果减去一个eax同样可以得到正确的值。

  • 假设 eax=3 计算 eax * 7 + 10 的结果,拆分过程如下:
  • 1.计算 lea edx,dword ptr ds:[eax * 8] 这就相当于计算 edx = (8 * eax)
  • 2.计算 sub edx,eax 这就相当于计算 edx = (8 * eax) - eax
  • 3.计算 add edx,10 这就相当于计算 edx = ( (8 * eax) - eax ) + 10
  • 4.经过如上计算,我们就可以计算出eax * 7 + 10的最终结果

这个计算过程看似复杂,但其实在汇编层面并不难构建,如下分别实现计算两个表达式求值过程。

.data  x DWORD ?  szFmt BYTE '计算结果: %d',0dh,0ah,0.code  main PROC    ; 针对乘法的lea指令优化    mov dword ptr ds:[x],3        ; 如果计算乘法时乘数非2的次幂,则此时需要减    ; 计算 edx = eax * 7 + 10    mov eax,dword ptr ds:[x]               ; eax = 3 => 计算 eax * 7 + 10    lea edx,dword ptr ds:[eax * 8]         ; edx = eax * 8    sub edx,eax; edx = edx - eax    add edx,10 ; edx = edx + 10    invoke crt_printf,addr szFmt,edx       ; edx = eax * 7 + 10        ; 计算 edx = eax * 3 - 7    mov eax,dword ptr ds:[x]               ; eax = 3 => 计算 eax * 3 - 7    lea edx,dword ptr ds:[eax * 2]         ; edx = eax * 2    add edx,eax; edx = edx + eax    sub edx,7  ; edx = edx - 7    invoke crt_printf,addr szFmt,edx       ; edx = eax * 3 - 7        invoke ExitProcess,0  main ENDPEND main

7.5 使用SHL计算无符号乘法

通过使用逻辑左移同样可以实现2的次幂的高速乘法运算,但逻辑左移只能用于计算无符号乘法,且只能计算被乘数是2的次方的算式。

  • 计算时我们需要参考次方表,这里我列举出几个常用的次方数值:

  • 次方表: 1=>2 2=>4 3=>8 4=>16 5=>32 6=>64 7=>128

  • 次方表: 8=>256 9=>512 10=>1024 11=>2048 12=>4096 13=>8192 14=>16384

  • 假设 eax=3 计算 eax * 8 + 10 的结果,拆分过程如下:

  • 计算 shl eax,3 这就相当于计算 eax = eax * 2 ^(次方) 3 其公式相当于计算 eax = eax * 8

  • 计算 add eax,10 这就相当于计算 eax = (eax * 8) + 10

  • 最终即可得到计算结果也就是3*8+10得到34

通过使用逻辑左移,我们可以实现快速无符号乘法运算,如下代码是效率最高的一种。

.data  x DWORD ?  szFmt BYTE '计算结果: %d',0dh,0ah,0.code  main PROC    mov dword ptr ds:[x],3        ; 计算 eax = eax * 2 ^ 1 相当于计算 eax * 2    mov eax,dword ptr ds:[x]    shl eax,1    invoke crt_printf,addr szFmt,eax        ; 计算 eax = eax * 2 ^ 2 相当于计算 eax * 4    mov eax,dword ptr ds:[x]    shl eax,2    invoke crt_printf,addr szFmt,eax        ; 计算 eax = eax * 2 ^ 3 相当于计算 eax * 8    mov eax,dword ptr ds:[x]    shl eax,3    add eax,10    invoke crt_printf,addr szFmt,eax    invoke ExitProcess,0  main ENDPEND main

7.6 使用SAL计算有符号乘法

通过使用算数左移同样可以实现2的次幂的高速乘法运算,与逻辑左移不同,算术左移只能计算有符号乘法,且只能计算被乘数是2的次方的算式。

  • 计算时我们需要参考次方表,这里我列举出几个常用的次方数值:

  • 次方表: 1=>2 2=>4 3=>8 4=>16 5=>32 6=>64 7=>128

  • 次方表: 8=>256 9=>512 10=>1024 11=>2048 12=>4096 13=>8192 14=>16384

  • 假设 eax=-5,ebx=3 计算 (eax * 8) + (ebx * 4) 的结果,拆分过程如下:

  • 计算 sal eax,3 这就相当于计算 eax = (eax * 2 ^ 3 ) 其公式相当于计算 eax = eax * 8 结果是一个有符号数

  • 计算 shl ebx,2 这就相当于计算 ebx = (ebx * 2 ^2) 其公式相当于计算 ebx = ebx * 4 结果是一个无符号数

  • 最终将有符号与无符号数通过 add eax,ebx 相加,即可得到(eax * 8) + (ebx * 4)的最终结果-28

如下是通过算数左移,实现2的次幂的高速乘法运算,我们可以将算数运算与逻辑运算相加通过此方式提高运算效率。

.data  x DWORD ?  y DWORD ?  szFmt BYTE '计算结果: %d',0dh,0ah,0.code  main PROC    mov dword ptr ds:[x],-5    mov dword ptr ds:[y],3        ; 计算 eax = eax * 2 ^ 1 相当于计算 eax * 2    mov eax,dword ptr ds:[x]    sal eax,1    invoke crt_printf,addr szFmt,eax        ; 计算 eax = eax * 2 ^ 2 相当于计算 eax * 4    mov eax,dword ptr ds:[x]    sal eax,2    invoke crt_printf,addr szFmt,eax    ; 计算 eax = (eax * 2 ^ 3 ) + (ebx * 2 ^2) 相当于计算 (eax * 8) + (ebx * 4)    mov eax,dword ptr ds:[x]    mov ebx,dword ptr ds:[y]    sal eax,3                  ; eax * 8 (有符号乘法)    shl ebx,2                  ; ebx * 4 (无符号乘法)    add eax,ebx                ; eax + ebx    invoke crt_printf,addr szFmt,eax    invoke ExitProcess,0  main ENDPEND main

乘法优化的知识点基本就这些,除了两个未知变量的相乘无法优化外,其他形式的乘法运算均可以进行优化,如果表达式中存在一个常量值,那编译器则会匹配各种优化策略,最后对不符合优化策略的运算进行调整,如果真的无法优化,则会使用原始乘法指令计算。

本文作者: 王瑞
本文链接: https://www.lyshark.com/post/ade8241c.html
版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!

来源地址:https://blog.csdn.net/lyshark_csdn/article/details/132444854

--结束END--

本文标题: 5.7 汇编语言:汇编高效乘法运算

本文链接: https://lsjlt.com/news/382515.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • 5.7 汇编语言:汇编高效乘法运算
    乘法指令是一种在CPU中实现的基本算术操作,用于计算两个数的乘积。在汇编语言中,乘法指令通常是通过mul(无符号乘法)和imul(有符号乘法)这两个指令实现的。由于乘法指令在执行时所消耗的时钟周期较多,所以编译器在优化代码时通常会尝试将乘法...
    99+
    2023-08-30
    汇编 汇编语言 Windows汇编 x86
  • 汇编语言系列之汇编实现简单数学运算
    目录1.计算S=1+2×3+3×4+4×5+···+N(N+1)1.1设计要求:1.2设计思路:1.3程序清单:1.4程序运行结果及分析:2.计算N!2.1设计要求:2.2设计思路:...
    99+
    2024-04-02
  • 汇编语言是不是高级语言
    本篇内容介绍了“汇编语言是不是高级语言”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!汇编语言不是高级语言;它和机器语言一样,都属于低级语言。...
    99+
    2023-07-05
  • 汇编语言功能用循环累加实现乘法
    目录问题1:编程计算2的2次方,结果存在ax中分析:用2+2实现问题2:编程实现2的12次方分析:用loop实现问题3:编程实现123*236,结果存在ax中分析:用236相加123...
    99+
    2024-04-02
  • 汇编语言怎么实现简单数学运算
    这篇文章主要介绍“汇编语言怎么实现简单数学运算”,在日常操作中,相信很多人在汇编语言怎么实现简单数学运算问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”汇编语言怎么实现简单数学运算”的疑惑有所帮助!接下来,请跟...
    99+
    2023-06-21
  • 汇编语言系列之汇编实现字符串操作
    目录1.字符串匹配设计1.1设计要求:1.2设计思路:1.3程序清单:1.4程序运行结果及分析:2.字符串输入及显示设计2.1设计要求:2.2设计思路:2.3程序清单:2.4程序运行...
    99+
    2024-04-02
  • 汇编语言:x86汇编指令大全及其注意事项
    目录Part 1:instructionPart 22.1 (逻辑)运算、移位等常用指令2.1 (逻辑)运算、移位等常用指令2.2 循环移位指令2.3 数据串操作指令2.4 逻辑运算...
    99+
    2024-04-02
  • 汇编语言入门汇编指令及寄存器详解教程
    目录前言什么是汇编语言汇编语言产生的原因汇编与二进制的关系寄存器寄存器作用存取速度比较寄存器分类常用寄存器用途寄存器EAX、AX、AH、AL的关系汇编语言指令数据传送指令算术运算指令...
    99+
    2024-04-02
  • 详解汇编语言MOV指令
    MOV(Move)指令是汇编语言中最基本的指令之一,用于将数据从一个位置复制到另一个位置。它的语法形式通常是:MOV dest, s...
    99+
    2023-08-14
    汇编语言
  • 机器语言、汇编语言和高级语言有哪些区别
    这篇文章主要为大家展示了“机器语言、汇编语言和高级语言有哪些区别”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“机器语言、汇编语言和高级语言有哪些区别”这篇文章吧。区别:1、机器语言的执行效率高,...
    99+
    2023-06-29
  • x86汇编_MUL/IMUL乘法指令_笔记52
    32位模式下整数乘法可以实现32、16或8位的操作,64位下还可以使用64位操作数。MUL执行无符号乘法,IMUL执行有符号乘法。 MUL指令:无符号数乘法 32 位模式下,MUL(无符号数乘法)指令有三种类型: 执行 8 位操作数与 AL...
    99+
    2023-10-24
    java 开发语言
  • C语言中调用汇编语言详解
    目录1.建立新工程2.改写程序3.总结1.建立新工程 首先点击Project里面的 New uVision Project 然后输入文件名,点击保存即可。 在你命名的project中...
    99+
    2024-04-02
  • 计算机网络中机器语言、汇编语言、高级语言各有什么特点
    这篇文章主要介绍计算机网络中机器语言、汇编语言、高级语言各有什么特点,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!机器语言的特点:难学、难懂、难理解;无通用性;需要人为分配内存;运行速度最快。汇编语言的特点:程序的执...
    99+
    2023-06-14
  • 汇编语言无法在云主机上运行怎么解决
    汇编语言无法在云主机上运行的原因可能是因为云主机的操作系统不支持直接运行汇编代码。解决办法可以有以下几种:1. 使用虚拟机:在云主机...
    99+
    2023-09-20
    云主机
  • c语言switch反汇编的实现
    目录一、switch语句1、在正向编码时,switch语句可以看做是if语句的简写2、break在switch语句的妙用二、switch语句的反汇编1、当switch存在3个分支时2...
    99+
    2024-04-02
  • 汇编语言开发过程详解
    目录一、逐步开发(1)源程序的编辑(2)源程序的汇编(3)目标文件的连接(4)可执行文件的运行二、列表文件与调试程序(1)列表文件(2)调试程序三、快速开发程序开发过程 一、逐步开...
    99+
    2024-04-02
  • 汇编语言指令大全 X86和X87汇编指令大全(带注释)
    目录一、数据传输指令1. 通用数据传送指令.2. 输入输出端口传送指令.3. 目的地址传送指令.4. 标志传送指令.二、算术运算指令三、逻辑运算指令四、串指令五、程序转移指令六、伪指...
    99+
    2024-04-02
  • 计算机系统汇编语言和机器语言深入理解
    目录引言1、机器语言2、汇编语言3、高级语言总结引言 《深入理解计算机系统》第三章——程序的机器级表示。作者首先讲解了汇编代码和机器代码的关系,阐述了汇编承...
    99+
    2024-04-02
  • 汇编语言的函数式编程实例分析
    这篇文章主要介绍了汇编语言的函数式编程实例分析的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇汇编语言的函数式编程实例分析文章都会有所收获,下面我们一起来看看吧。一切都是为了消除副作用要了解函数式编程,我们需要首...
    99+
    2023-06-27
  • 汇编语言和c语言的区别有哪些
    本文小编为大家详细介绍“汇编语言和c语言的区别有哪些”,内容详细,步骤清晰,细节处理妥当,希望这篇“汇编语言和c语言的区别有哪些”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。区别:1、因为汇编语言实质上是机器语言...
    99+
    2023-07-04
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作