最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會(huì)員登陸 & 注冊(cè)

5.7 匯編語(yǔ)言:匯編高效乘法運(yùn)算

2023-08-23 09:56 作者:bili_42682284418  | 我要投稿

匯編語(yǔ)言是一種面向機(jī)器的低級(jí)語(yǔ)言,用于編寫計(jì)算機(jī)程序。匯編語(yǔ)言與計(jì)算機(jī)機(jī)器語(yǔ)言非常接近,匯編語(yǔ)言程序可以使用符號(hào)、助記符等來(lái)代替機(jī)器語(yǔ)言的二進(jìn)制碼,但最終會(huì)被匯編器編譯成計(jì)算機(jī)可執(zhí)行的機(jī)器碼。


乘法指令是一種在CPU中實(shí)現(xiàn)的基本算術(shù)操作,用于計(jì)算兩個(gè)數(shù)的乘積。在匯編語(yǔ)言中,乘法指令通常是通過(guò)`mul(無(wú)符號(hào)乘法)`和`imul(有符號(hào)乘法)`這兩個(gè)指令實(shí)現(xiàn)的。由于乘法指令在執(zhí)行時(shí)所消耗的時(shí)鐘周期較多,所以編譯器在優(yōu)化代碼時(shí)通常會(huì)嘗試將乘法操作轉(zhuǎn)換為更高效的加法、和移位操作。


- 對(duì)于較小的數(shù),編譯器可能會(huì)選擇將乘法操作直接轉(zhuǎn)換為加法操作。例如,將表達(dá)式`a * b`轉(zhuǎn)換為`a + a + ... + a`(b次相加)的形式。這種方式可以通過(guò)循環(huán)展開(kāi)、代碼向量化等技術(shù)來(lái)優(yōu)化。


- 對(duì)于較大的數(shù),編譯器可能會(huì)使用位移和移位操作來(lái)代替乘法。例如,將表達(dá)式`a * b`轉(zhuǎn)換為`a << n + a << m`的形式,其中`n`和`m`為符合條件的位數(shù)。這種方式可以通過(guò)位移指令的高效性來(lái)加速運(yùn)算。


當(dāng)以上方式均無(wú)法進(jìn)行優(yōu)化時(shí),編譯器才會(huì)使用`mul/imul`指令來(lái)執(zhí)行乘法操作。這兩條指令可以對(duì)無(wú)符號(hào)數(shù)和有符號(hào)數(shù)進(jìn)行乘法運(yùn)算,即便這兩條指令會(huì)使用更多的時(shí)鐘周期,但乘法指令的計(jì)算效率相對(duì)于其他指令`DIV`來(lái)說(shuō)仍然較低,因此在編寫高效代碼時(shí),應(yīng)盡可能地避免使用乘法操作,并結(jié)合使用上面提到的技巧進(jìn)行優(yōu)化。


### 7.1 使用IMUL指令完成乘法


要計(jì)算乘法在不考慮執(zhí)行效率的情況下編譯器通常會(huì)直接使用`imul`指令完成計(jì)算,imul指令在一些情況下可以比其他乘法指令(如mul指令)更快地執(zhí)行乘法運(yùn)算,但性能較低的原因主要是由于imul指令通常用于有符號(hào)數(shù)的乘法運(yùn)算,并且在執(zhí)行時(shí)需要處理符號(hào)位的擴(kuò)展和溢出問(wèn)題,這轉(zhuǎn)換成了額外的指令和時(shí)鐘周期的消耗。如果對(duì)于無(wú)符號(hào)整數(shù)或需要使用寄存器的低位或者高位結(jié)果的情況,使用imul指令可以提供一定的優(yōu)勢(shì)。


計(jì)算乘法時(shí)應(yīng)遵循:


? - 如果乘數(shù)與被乘數(shù)都是`8位` 則把`AL`做乘數(shù),結(jié)果放在`AX`中

? - 如果乘數(shù)與被乘數(shù)都是`16位` 將把`AX`做乘數(shù),結(jié)果放在`EAX`中

? - 如果乘數(shù)與被乘數(shù)都是`32位` 將把`EAX`做乘數(shù),結(jié)果放在`EDX:EAX`中


乘法指令計(jì)算很簡(jiǎn)單,只需要累加乘數(shù)即可,如下所示則是一個(gè)簡(jiǎn)單的計(jì)算三個(gè)數(shù)相乘的匯編實(shí)現(xiàn);

```ASM

.data

? ? x DWORD ?

? ? y DWORD ?

? ? z DWORD ?

? ? szFmt BYTE '計(jì)算結(jié)果: %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

? ? ??

? ? ? ; 計(jì)算 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 ENDP

END main

```


### 7.2 使用LEA指令替換乘法


在實(shí)際編程中,我們可以使用LEA指令來(lái)替代乘法操作,從而提高代碼的執(zhí)行效率。但讀者需要注意,在使用LEA計(jì)算乘法時(shí)必須要保證乘數(shù)是`2`的次冪,并且乘數(shù)的范圍必須是`2/4/8`這三個(gè)區(qū)間才可使用該指令,我們使用匯編來(lái)實(shí)現(xiàn)計(jì)算`eax*8+2`其匯編指令如下。


?- 假設(shè) `eax=5` 計(jì)算 `eax * 8 + 2` 的結(jié)果,拆分過(guò)程如下:

? - 1.計(jì)算 `lea ebx,dword ptr ds:[eax * 8 + 2]` 這就相當(dāng)于計(jì)算 `ebx = (eax * 8) +2`直接可得到結(jié)果。


第一個(gè)案例比較簡(jiǎn)單,可直接使用一條lea指令即可完成計(jì)算過(guò)程,只要保證被乘數(shù)是2的次冪即可。

```ASM

.data

? x DWORD ?

? szFmt BYTE '計(jì)算結(jié)果: %d',0dh,0ah,0

.code

? main PROC

? ? ; 針對(duì)乘法的lea指令優(yōu)化

? ? 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 ENDP

END main

```


### 7.3 使用LEA指令拆分計(jì)算


如果我們計(jì)算的乘法超出了`2/4/8`次冪范圍,則需要對(duì)乘法進(jìn)行拆分,拆分時(shí)也應(yīng)遵循2的次冪原則,拆分后在分開(kāi)來(lái)計(jì)算。


?- 假設(shè) `eax=3` 計(jì)算 `15 * eax` 的結(jié)果,拆分過(guò)程如下:

? - 1.計(jì)算 `lea edx,[eax * 4 + eax]` 這就相當(dāng)于計(jì)算 `edx = (4 * eax) + eax = 5eax` 其中的每個(gè)`edx`就相當(dāng)于5個(gè)`eax`

? - 2.計(jì)算 `lea edx,[edx * 2 + edx]` 這就相當(dāng)于計(jì)算 `edx = (5 * eax) * 2 + (5 * eax)`

? - 3.計(jì)算 `(5eax * 2) = 10eax` 接著計(jì)算 `(5 * eax) = 5eax` 最后得出 `10eax + 5eax`

? - 4.經(jīng)過(guò)該過(guò)程可得出 `eax * 15 = 45` 最終計(jì)算`3*15=45`得到最終結(jié)果.


這個(gè)計(jì)算過(guò)程看似復(fù)雜,但如果將其轉(zhuǎn)化為匯編指令那么只需要兩條即可實(shí)現(xiàn)快速乘法運(yùn)算。

```ASM

.data

? x DWORD ?

? szFmt BYTE '計(jì)算結(jié)果: %d',0dh,0ah,0

.code

? main PROC

? ? ; 針對(duì)乘法的lea指令優(yōu)化

? ? mov dword ptr ds:[x],3

? ??

? ? ; 如果使用lea計(jì)算乘法,則乘數(shù)必須是2/4/8

? ? mov eax,dword ptr ds:[x]? ? ? ? ? ? ? ?; eax = 3

? ? lea edx,dword ptr ds:[eax * 4 + eax]? ?; edx = 4eax + eax 得出 5eax,也就是說(shuō)每一個(gè)edx就代表5個(gè)eax

? ? lea edx,dword ptr ds:[edx * 2 + edx]? ?; edx = (5eax * 2) + 5eax 最終得出 15eax

? ? invoke crt_printf,addr szFmt,edx? ? ? ?; edx = eax * 15 計(jì)算后得出 45

? ??

? ? invoke ExitProcess,0

? main ENDP

END main

```


### 7.4 使用LEA指令遞減計(jì)算


如果計(jì)算乘法時(shí)乘數(shù)非2的次冪,這種情況下需要減去特定的值,例如當(dāng)我們計(jì)算`eax * 7`時(shí),由于7非二的次冪,我們無(wú)法通過(guò)`lea`指令進(jìn)行計(jì)算,但我們可以計(jì)算`eax * 8`計(jì)算出的結(jié)果減去一個(gè)`eax`同樣可以得到正確的值。


?- 假設(shè) `eax=3` 計(jì)算 `eax * 7 + 10` 的結(jié)果,拆分過(guò)程如下:

? - 1.計(jì)算 `lea edx,dword ptr ds:[eax * 8]` 這就相當(dāng)于計(jì)算 `edx = (8 * eax)`

? - 2.計(jì)算 `sub edx,eax` 這就相當(dāng)于計(jì)算 `edx = (8 * eax) - eax`

? - 3.計(jì)算 `add edx,10` 這就相當(dāng)于計(jì)算 `edx = ( (8 * eax) - eax ) + 10`

? - 4.經(jīng)過(guò)如上計(jì)算,我們就可以計(jì)算出`eax * 7 + 10`的最終結(jié)果


這個(gè)計(jì)算過(guò)程看似復(fù)雜,但其實(shí)在匯編層面并不難構(gòu)建,如下分別實(shí)現(xiàn)計(jì)算兩個(gè)表達(dá)式求值過(guò)程。

```ASM

.data

? x DWORD ?

? szFmt BYTE '計(jì)算結(jié)果: %d',0dh,0ah,0

.code

? main PROC

? ? ; 針對(duì)乘法的lea指令優(yōu)化

? ? mov dword ptr ds:[x],3

? ??

? ? ; 如果計(jì)算乘法時(shí)乘數(shù)非2的次冪,則此時(shí)需要減


? ? ; 計(jì)算 edx = eax * 7 + 10

? ? mov eax,dword ptr ds:[x]? ? ? ? ? ? ? ?; eax = 3 => 計(jì)算 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

? ??

? ? ; 計(jì)算 edx = eax * 3 - 7

? ? mov eax,dword ptr ds:[x]? ? ? ? ? ? ? ?; eax = 3 => 計(jì)算 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 ENDP

END main

```


### 7.5 使用SHL計(jì)算無(wú)符號(hào)乘法


通過(guò)使用邏輯左移同樣可以實(shí)現(xiàn)2的次冪的高速乘法運(yùn)算,但邏輯左移只能用于計(jì)算無(wú)符號(hào)乘法,且只能計(jì)算被乘數(shù)是2的次方的算式。


?- 計(jì)算時(shí)我們需要參考次方表,這里我列舉出幾個(gè)常用的次方數(shù)值:

? - 次方表: 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


?- 假設(shè) `eax=3` 計(jì)算 `eax * 8 + 10` 的結(jié)果,拆分過(guò)程如下:

? - 1.計(jì)算 `shl eax,3` 這就相當(dāng)于計(jì)算 `eax = eax * 2 ^(次方) 3` 其公式相當(dāng)于計(jì)算 `eax = eax * 8`

? - 2.計(jì)算 `add eax,10` 這就相當(dāng)于計(jì)算 `eax = (eax * 8) + 10`

? - 3.最終即可得到計(jì)算結(jié)果也就是`3*8+10`得到34


通過(guò)使用邏輯左移,我們可以實(shí)現(xiàn)快速無(wú)符號(hào)乘法運(yùn)算,如下代碼是效率最高的一種。

```ASM

.data

? x DWORD ?

? szFmt BYTE '計(jì)算結(jié)果: %d',0dh,0ah,0

.code

? main PROC

? ? mov dword ptr ds:[x],3

? ??

? ? ; 計(jì)算 eax = eax * 2 ^ 1 相當(dāng)于計(jì)算 eax * 2

? ? mov eax,dword ptr ds:[x]

? ? shl eax,1

? ? invoke crt_printf,addr szFmt,eax

? ??

? ? ; 計(jì)算 eax = eax * 2 ^ 2 相當(dāng)于計(jì)算 eax * 4

? ? mov eax,dword ptr ds:[x]

? ? shl eax,2

? ? invoke crt_printf,addr szFmt,eax

? ??

? ? ; 計(jì)算 eax = eax * 2 ^ 3 相當(dāng)于計(jì)算 eax * 8

? ? mov eax,dword ptr ds:[x]

? ? shl eax,3

? ? add eax,10

? ? invoke crt_printf,addr szFmt,eax


? ? invoke ExitProcess,0

? main ENDP

END main

```


### 7.6 使用SAL計(jì)算有符號(hào)乘法


通過(guò)使用算數(shù)左移同樣可以實(shí)現(xiàn)2的次冪的高速乘法運(yùn)算,與邏輯左移不同,算術(shù)左移只能計(jì)算有符號(hào)乘法,且只能計(jì)算被乘數(shù)是2的次方的算式。


?- 計(jì)算時(shí)我們需要參考次方表,這里我列舉出幾個(gè)常用的次方數(shù)值:

? - 次方表: 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


?- 假設(shè) `eax=-5,ebx=3` 計(jì)算 `(eax * 8) + (ebx * 4)` 的結(jié)果,拆分過(guò)程如下:

? - 1.計(jì)算 `sal eax,3` 這就相當(dāng)于計(jì)算 `eax = (eax * 2 ^ 3 )` 其公式相當(dāng)于計(jì)算 `eax = eax * 8` 結(jié)果是一個(gè)有符號(hào)數(shù)

? - 2.計(jì)算 `shl ebx,2` 這就相當(dāng)于計(jì)算 `ebx = (ebx * 2 ^2)` 其公式相當(dāng)于計(jì)算 `ebx = ebx * 4` 結(jié)果是一個(gè)無(wú)符號(hào)數(shù)

? - 3.最終將有符號(hào)與無(wú)符號(hào)數(shù)通過(guò) `add eax,ebx` 相加,即可得到`(eax * 8) + (ebx * 4)`的最終結(jié)果-28


如下是通過(guò)算數(shù)左移,實(shí)現(xiàn)2的次冪的高速乘法運(yùn)算,我們可以將算數(shù)運(yùn)算與邏輯運(yùn)算相加通過(guò)此方式提高運(yùn)算效率。

```ASM

.data

? x DWORD ?

? y DWORD ?

? szFmt BYTE '計(jì)算結(jié)果: %d',0dh,0ah,0

.code

? main PROC

? ? mov dword ptr ds:[x],-5

? ? mov dword ptr ds:[y],3

? ??

? ? ; 計(jì)算 eax = eax * 2 ^ 1 相當(dāng)于計(jì)算 eax * 2

? ? mov eax,dword ptr ds:[x]

? ? sal eax,1

? ? invoke crt_printf,addr szFmt,eax

? ??

? ? ; 計(jì)算 eax = eax * 2 ^ 2 相當(dāng)于計(jì)算 eax * 4

? ? mov eax,dword ptr ds:[x]

? ? sal eax,2

? ? invoke crt_printf,addr szFmt,eax


? ? ; 計(jì)算 eax = (eax * 2 ^ 3 ) + (ebx * 2 ^2) 相當(dāng)于計(jì)算 (eax * 8) + (ebx * 4)

? ? mov eax,dword ptr ds:[x]

? ? mov ebx,dword ptr ds:[y]

? ? sal eax,3? ? ? ? ? ? ? ? ? ; eax * 8 (有符號(hào)乘法)

? ? shl ebx,2? ? ? ? ? ? ? ? ? ; ebx * 4 (無(wú)符號(hào)乘法)

? ? add eax,ebx? ? ? ? ? ? ? ? ; eax + ebx

? ? invoke crt_printf,addr szFmt,eax


? ? invoke ExitProcess,0

? main ENDP

END main

```


乘法優(yōu)化的知識(shí)點(diǎn)基本就這些,除了兩個(gè)未知變量的相乘無(wú)法優(yōu)化外,其他形式的乘法運(yùn)算均可以進(jìn)行優(yōu)化,如果表達(dá)式中存在一個(gè)常量值,那編譯器則會(huì)匹配各種優(yōu)化策略,最后對(duì)不符合優(yōu)化策略的運(yùn)算進(jìn)行調(diào)整,如果真的無(wú)法優(yōu)化,則會(huì)使用原始乘法指令計(jì)算。


本文作者: 王瑞

本文鏈接: https://www.lyshark.com/post/ade8241c.html

版權(quán)聲明: 本博客所有文章除特別聲明外,均采用 BY-NC-SA 許可協(xié)議。轉(zhuǎn)載請(qǐng)注明出處!


5.7 匯編語(yǔ)言:匯編高效乘法運(yùn)算的評(píng)論 (共 條)

分享到微博請(qǐng)遵守國(guó)家法律
博罗县| 青海省| 周至县| 兴文县| 武义县| 怀来县| 涞源县| 离岛区| 遂昌县| 西城区| 临武县| 镇原县| 海兴县| 西乌珠穆沁旗| 天长市| 毕节市| 醴陵市| 乐陵市| 舟曲县| 房山区| 武汉市| 遂昌县| 荆州市| 从化市| 淅川县| 宁陵县| 龙岩市| 开化县| 垫江县| 广德县| 卓尼县| 葵青区| 方山县| 象山县| 定陶县| 通州市| 邯郸县| 哈巴河县| 墨竹工卡县| 满城县| 耒阳市|