0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線(xiàn)課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

編譯器如何對(duì)代碼進(jìn)行優(yōu)化(下)

jf_78858299 ? 來(lái)源:看雪論壇 彼岸風(fēng) ? 作者:看雪論壇 彼岸風(fēng) ? 2023-02-01 16:25 ? 次閱讀

變量乘常量

  • 常量為2的冪

乘法將會(huì)被替換為執(zhí)行周期更短的移位指令。

int fun(int n) {
    return n * 16;
}
// mov eax, n
// shl eax, 4
  • 常量為非2的冪

因?yàn)?thumb 和 x86 指令集的差異,安卓平臺(tái)上處理的更好一些。

我并不推薦你把自己當(dāng)成編譯器,看到算式想著怎么轉(zhuǎn)成匯編,而是推薦記下這種算法,看到計(jì)算過(guò)程知道怎么轉(zhuǎn)成原式,當(dāng)然也不追求100%還原,邏輯一致即可。

編譯器會(huì)對(duì)非2的冪進(jìn)行拆解,例如:

  • n * 15 = n * 16 - n = n << 4 - n
  • n * 12 = n * 3 * 4 = (n << 1 + n) << 2
int value = n * 15;
// rsb.w r0, r1, r1, lsl #4

int value = n * 12;
// add.w r0, r1, r1, lsl #1

當(dāng)然 windows 平臺(tái)也不是一無(wú)是處,某些乘法會(huì)通過(guò) lea 將兩條指令合并成一條。

  • n * 4 + 5 = lea edx, [ecx * 4 + 5]
printf("%d", n * 4 + 5);
// mov ecx, n
// lea edx, [ecx * 4 + 5]
// push edx

至于值為不可拆分的素?cái)?shù),就改用 mul 指令。

變量乘變量

這一步?jīng)]有什么優(yōu)化空間,因?yàn)槎际俏粗?,只能老老?shí)實(shí)用 mul 指令。

int fun(int n, int m) {
    return n * m;
}
// mov eax, n
// mov ecx, m
// imul ecx

除法

在看下面內(nèi)容之前,不妨再問(wèn)問(wèn)自己,真的了解除法嗎?除法的本質(zhì)是什么?

ok,現(xiàn)在是復(fù)習(xí)時(shí)間,簡(jiǎn)單總結(jié)一下以下兩個(gè)問(wèn)題。

  • 符號(hào)問(wèn)題
    1. 兩個(gè)無(wú)符號(hào)整數(shù)相除,結(jié)果依然是無(wú)符號(hào)
    2. 兩個(gè)有符號(hào)整數(shù)相除,結(jié)果依然是有符號(hào)
    3. 混除,參數(shù)全被當(dāng)成無(wú)符號(hào)計(jì)算,結(jié)果是無(wú)符號(hào)
  • 取整問(wèn)題
    1. 向下取整 —— floor 函數(shù) 存在誤差 => ( - a / b ) + ( a / b ) != - ( a / b ) - ( a / b )
    2. 向上取整 —— ceil 函數(shù) 存在誤差 => ( - a / b ) != - ( a / b )
    3. 向零取整 —— 截?cái)喑?Truncate),可以理解為放棄小數(shù)部分,只取整數(shù)部分,可以在任何情況保持恒等,大部分語(yǔ)言用的都是截?cái)喑?/li>

除數(shù)為無(wú)符號(hào)數(shù)

  • 大數(shù)(負(fù)數(shù))

在無(wú)符號(hào)中,負(fù)數(shù)的值是很大的,例如 -8 = 0xFFFFFFF8。

而除以這種大數(shù),只能出現(xiàn)兩種情況,1或 0,換個(gè)思路來(lái)想就可以寫(xiě)成這樣:[被除數(shù)] >= [除數(shù)] ? 1 : 0

我們來(lái)看看 thumb 下是怎么優(yōu)化的?

UINT value = (UINT)n / -8;
// cmn.w r0, #9    ; cmp r0, -9
// it hi
// movhi r1, #1    ; n > -9 ? 1 : 0

他這里做了一個(gè)小小的變形:[被除數(shù)] > [除數(shù) - 1] ? 1 : 0,邏輯上仍然成立。

  • 2的冪

簡(jiǎn)單的移位

UINT value = (UINT)n / 4;
// lsrs r1, r0, #2
  • 非2的冪

接下來(lái)就要引入一個(gè)非常魔幻的設(shè)定,magic number。說(shuō)來(lái)這個(gè)魔數(shù),依稀記得早在幾年前的知乎上看到過(guò)一篇文章,講的是雷神之錘游戲引擎就使用了這么一個(gè)魔數(shù),那時(shí)的cpu是非常低效的,而為了避免使用除法這種 cpu 周期偏長(zhǎng)的指令,天才的程序員們想出了各種奇技淫巧,其中最為后人津津樂(lè)道的就是游戲中對(duì)平方根倒數(shù)的優(yōu)化,將計(jì)算過(guò)程等價(jià)替換為加法和移位操作,損失少量的精度來(lái)?yè)Q取絕對(duì)的性能。

我們這里的魔數(shù)稍有不同,它是用來(lái)優(yōu)化除法的,而且邏輯上也相對(duì)容易理解一些,廢話(huà)不多說(shuō),進(jìn)入正題。

對(duì)于普通除法,我們可以得到以下的換算:(x => 被除數(shù)變量,c => 除數(shù)常量,M => 魔數(shù))

假設(shè)用 M 代替 2^n / c 這個(gè) Magic 變量,于是有:

也就是說(shuō),除法將會(huì)被轉(zhuǎn)會(huì)成 (x * M) >> n 的邏輯進(jìn)行運(yùn)算,至于 M 和 n 值怎么來(lái)的,我們不關(guān)心,這是編譯器根據(jù)除數(shù)算出來(lái)的最優(yōu)值,會(huì)盡力保證偏差達(dá)到最小,我們要做的是認(rèn)出魔數(shù)和移了多少位,然后根據(jù) m = 2^n/c 公式求得原本的除數(shù) c = 2^n/m

公式來(lái)源于《C++反匯編與逆向分析技術(shù)揭秘》,真的是非常非常的細(xì),書(shū)中整個(gè)推導(dǎo)過(guò)程很完整,很建議各位去仔細(xì)研讀一遍

以下代碼為例:

printf("%u", (unsigned)argc / 3);
// mov eax, 0xAAAAAAAB   ; M
// mul [argc]            ; edx:eax = argc * M
// shr edx, 1            ; edx = argc * M >> 32 >> 1
// push edx

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 代碼
    +關(guān)注

    關(guān)注

    30

    文章

    4801

    瀏覽量

    68730
  • 編譯器
    +關(guān)注

    關(guān)注

    1

    文章

    1636

    瀏覽量

    49172
  • Andorid
    +關(guān)注

    關(guān)注

    0

    文章

    7

    瀏覽量

    6998
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    如何編寫(xiě)有利于編譯器優(yōu)化代碼

    對(duì)于嵌入式系統(tǒng),最終代碼的體積和效率取決于由編譯器生成的可執(zhí)行代碼,而非開(kāi)發(fā)人員編寫(xiě)的源代碼;但是源代碼
    發(fā)表于 11-09 10:31 ?1415次閱讀
    如何編寫(xiě)有利于<b class='flag-5'>編譯器</b><b class='flag-5'>優(yōu)化</b>的<b class='flag-5'>代碼</b>

    如何編寫(xiě)有利于編譯器優(yōu)化代碼

    本篇文章將以國(guó)際知名編譯器廠商IAR Systems的編譯器為例,來(lái)解答開(kāi)發(fā)人員在實(shí)際工作中常常遇到的問(wèn)題,工程師朋友們可以在IAR編譯器進(jìn)行實(shí)踐驗(yàn)證。
    發(fā)表于 08-01 09:43 ?493次閱讀
    如何編寫(xiě)有利于<b class='flag-5'>編譯器</b><b class='flag-5'>優(yōu)化</b>的<b class='flag-5'>代碼</b>

    SIMD計(jì)算機(jī)的優(yōu)化編譯器設(shè)計(jì)

    利用處理的相關(guān)資源,提高編譯器優(yōu)化性能和增強(qiáng)代碼可適應(yīng)性是SIMD處理優(yōu)化
    發(fā)表于 04-03 08:47 ?30次下載

    Keil C編譯器編程規(guī)則和代碼優(yōu)化

    本內(nèi)容介紹了Keil C編譯器編程規(guī)則和代碼優(yōu)化,要實(shí)用好單片機(jī)就必須清楚它的內(nèi)部結(jié)構(gòu)組織結(jié)構(gòu),無(wú)論是在芯片的選擇還是代碼的編寫(xiě)
    發(fā)表于 04-20 17:37 ?315次下載
    Keil C<b class='flag-5'>編譯器</b>編程規(guī)則和<b class='flag-5'>代碼</b><b class='flag-5'>優(yōu)化</b>

    編譯器_keil的優(yōu)化選項(xiàng)問(wèn)題

    keil編譯器優(yōu)化選項(xiàng)針對(duì)ARM,對(duì)STM32編譯的一些優(yōu)化的問(wèn)題
    發(fā)表于 02-25 14:18 ?3次下載

    C編譯器及其優(yōu)化

    本章將幫助讀者在ARM處理上編寫(xiě)高效的C代碼。本章涉及的一些技術(shù)不僅適用于ARM處理,也適用于其他RISC處理。本章首先從ARM編譯器
    發(fā)表于 10-17 17:22 ?2次下載

    編譯器優(yōu)化對(duì)函數(shù)的影響

    編譯器如gcc,可以指定不同的優(yōu)化參數(shù),在某些條件,有些函數(shù)可能會(huì)被優(yōu)化掉。
    的頭像 發(fā)表于 06-22 14:58 ?2847次閱讀
    <b class='flag-5'>編譯器</b><b class='flag-5'>優(yōu)化</b>對(duì)函數(shù)的影響

    如何編寫(xiě)有利于編譯器優(yōu)化代碼

    對(duì)于嵌入式系統(tǒng),最終代碼的體積和效率取決于由編譯器生成的可執(zhí)行代碼,而非開(kāi)發(fā)人員編寫(xiě)的源代碼;但是源代碼
    的頭像 發(fā)表于 03-29 15:58 ?1493次閱讀
    如何編寫(xiě)有利于<b class='flag-5'>編譯器</b><b class='flag-5'>優(yōu)化</b>的<b class='flag-5'>代碼</b>

    編譯器如何對(duì)代碼進(jìn)行優(yōu)化(上)

    在學(xué)習(xí) Andorid 逆向的過(guò)程中,發(fā)現(xiàn)無(wú)論是哪種編譯器,生成哪個(gè)平臺(tái)的代碼,其優(yōu)化思路在本質(zhì)上如出一轍,在 Windwos 平臺(tái)所使用的技巧,在安卓平臺(tái)仍然適用,不外乎乘法除法計(jì)算的優(yōu)化
    的頭像 發(fā)表于 02-01 16:25 ?921次閱讀

    編譯器優(yōu)化選項(xiàng)

    這一點(diǎn),需要了解編譯器的能力和限制;第三,要了解硬件的運(yùn)行方式,針對(duì)硬件特性進(jìn)行優(yōu)化。本文著重展開(kāi)第二點(diǎn)和第三點(diǎn)。 簡(jiǎn)單認(rèn)識(shí)編譯器 要寫(xiě)出高性能的
    的頭像 發(fā)表于 11-24 15:37 ?921次閱讀
    <b class='flag-5'>編譯器</b>的<b class='flag-5'>優(yōu)化</b>選項(xiàng)

    Keil編譯器優(yōu)化方法

    我們都知道,代碼是可以通過(guò)編譯器優(yōu)化的,有的時(shí)候,為了提高運(yùn)行速度或者減少代碼尺寸,會(huì)開(kāi)啟優(yōu)化選項(xiàng)。
    的頭像 發(fā)表于 10-23 16:35 ?648次閱讀
    Keil<b class='flag-5'>編譯器</b><b class='flag-5'>優(yōu)化</b>方法

    Triton編譯器與其他編譯器的比較

    的GPU編程框架,使開(kāi)發(fā)者能夠編寫(xiě)出接近手工優(yōu)化的高性能GPU內(nèi)核。 其他編譯器 (如GCC、Clang、MSVC等): 定位:通用編譯器,支持多種編程語(yǔ)言,廣泛應(yīng)用于各種軟件開(kāi)發(fā)場(chǎng)景。 目標(biāo):提供穩(wěn)定、高效的
    的頭像 發(fā)表于 12-24 17:25 ?382次閱讀

    Triton編譯器優(yōu)化技巧

    在現(xiàn)代計(jì)算環(huán)境中,編譯器的性能對(duì)于軟件的運(yùn)行效率至關(guān)重要。Triton 編譯器作為一個(gè)先進(jìn)的編譯器框架,提供了一系列的優(yōu)化技術(shù),以確保生成的代碼
    的頭像 發(fā)表于 12-25 09:09 ?236次閱讀

    Triton編譯器如何提升編程效率

    開(kāi)發(fā)者能夠更快地開(kāi)發(fā)出更高效的軟件。 1. 代碼優(yōu)化 1.1 編譯時(shí)優(yōu)化 Triton 編譯器編譯
    的頭像 發(fā)表于 12-25 09:12 ?239次閱讀

    Triton編譯器與GPU編程的結(jié)合應(yīng)用

    Triton編譯器簡(jiǎn)介 Triton編譯器是一種針對(duì)并行計(jì)算優(yōu)化編譯器,它能夠自動(dòng)將高級(jí)語(yǔ)言代碼轉(zhuǎn)換為針對(duì)特定硬件
    的頭像 發(fā)表于 12-25 09:13 ?246次閱讀