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

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

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

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

li5236 ? 來源:IAR Systems ? 作者:IAR Systems ? 2022-03-29 15:58 ? 次閱讀

嵌入式開發(fā)中,代碼的體積和運(yùn)行效率非常重要,代碼體積往往和芯片的FLASH、RAM容量對(duì)應(yīng),程序的運(yùn)行效率也要求在相應(yīng)能力的處理器上運(yùn)行。在大多數(shù)情況下,成熟的開發(fā)人員都希望降低代碼體積、提高代碼運(yùn)行效率,然而具體該怎么做呢?本篇文章將以國際知名編譯器廠商IAR Systems的編譯器為例,來解答開發(fā)人員在實(shí)際工作中常常遇到的問題,工程師朋友們可以在IAR編譯器上進(jìn)行實(shí)踐驗(yàn)證。

對(duì)于嵌入式系統(tǒng),最終代碼的體積和效率取決于由編譯器生成的可執(zhí)行代碼,而非開發(fā)人員編寫的源代碼;但是源代碼的優(yōu)化,可以幫助編譯器生成更加優(yōu)質(zhì)的可執(zhí)行代碼。因此,開發(fā)人員不僅要從整體效率等因素上去構(gòu)思源代碼體系,也要高度關(guān)注編譯器的性能和編譯優(yōu)化的便捷性。

有優(yōu)化功能的編譯器可生成既小又快的可執(zhí)行代碼,編譯器是通過對(duì)源代碼的重復(fù)轉(zhuǎn)換來實(shí)現(xiàn)優(yōu)化。通常,編譯器優(yōu)化會(huì)遵循完善的數(shù)學(xué)或邏輯理論基礎(chǔ)。但是某些編譯優(yōu)化則是通過啟發(fā)式的方法,經(jīng)驗(yàn)表明,一些代碼轉(zhuǎn)換往往會(huì)產(chǎn)生更好的代碼,或者開拓出進(jìn)一步編譯優(yōu)化的空間。

編譯優(yōu)化只有少數(shù)情況依賴于編譯器的黑科技,大多數(shù)時(shí)候編寫源代碼的方式?jīng)Q定了程序是否可以被編譯器優(yōu)化。在某些情況下,即使對(duì)源代碼做微小改動(dòng)也會(huì)對(duì)編譯器生成的代碼效率產(chǎn)生重大影響。

本文將講述在編寫代碼時(shí)需要注意的事項(xiàng),但我們首先應(yīng)明確一點(diǎn),我們沒有必要盡量減少代碼量,因?yàn)榧词乖谝粋€(gè)表達(dá)式中使用 ?:- 表達(dá)式、后增量和逗號(hào)表達(dá)式來消除副作用,也不會(huì)使編譯器產(chǎn)生更有效的代碼。這只會(huì)使你的源代碼變得晦澀難懂,難以維護(hù)。例如在一個(gè)復(fù)雜的表達(dá)式中間加入一個(gè)后增量或賦值,則在讀代碼的時(shí)候很容易被忽略。請(qǐng)盡量用一種易于閱讀的風(fēng)格來編寫代碼。

循環(huán)

下面看似簡單的循環(huán)會(huì)報(bào)錯(cuò)嗎?

for (i = 0; i ?。?n; ++i)

a[i] = b[i];

雖然不會(huì)報(bào)錯(cuò),但其中有幾點(diǎn)會(huì)影響到編譯器生成的代碼效率。

例如,索引變量的類型應(yīng)與指針相匹配。

像 a[i] 這樣的數(shù)組表達(dá)式實(shí)際上是 *(&a[0]+i*sizeof(a[0]),或者通俗地說:將第 i個(gè)元素的偏移量加到 a 的第一個(gè)元素的指針上。對(duì)于指針運(yùn)算, 索引表達(dá)式的類型最好與指針?biāo)赶虻念愋鸵恢拢ǎ撸遞ar 指針除外,因?yàn)槠渲羔標(biāo)赶虻念愋秃退饕磉_(dá)式的類型不同)。如果索引表達(dá)式的類型與指針?biāo)赶虻念愋筒黄ヅ?,那么在把它與指針相加之前,必須將它強(qiáng)制轉(zhuǎn)換為正確的類型。

如果在應(yīng)用中,堆棧空間資源(堆棧一般放在RAM中)比代碼尺寸資源(代碼一般放在ROM或者Flash中)更寶貴,則可以為索引變量選擇一個(gè)更小的類型來減少堆??臻g的使用,但這往往會(huì)犧牲代碼尺寸和執(zhí)行時(shí)間(代碼尺寸變大,執(zhí)行時(shí)間變慢)。不僅如此,這種轉(zhuǎn)換也會(huì)妨礙循環(huán)代碼的優(yōu)化。

除上述問題外,我們也要關(guān)注循環(huán)條件,因?yàn)橹挥性谶M(jìn)入循環(huán)之前可以計(jì)算出迭代次數(shù)的情況下,才可以進(jìn)行循環(huán)優(yōu)化。然而,這項(xiàng)計(jì)算工作非常復(fù)雜,并非用最終值減去初始值并除以增量那么簡單。例如,如果 i 是一個(gè)無符號(hào)字符,n 是一個(gè)整數(shù),而 n 的值是 1000,那么會(huì)發(fā)生什么情況?答案是變量 i 在達(dá)到 1000 之前就會(huì)溢出。

雖然程序員肯定不想要一個(gè)無限循環(huán),重復(fù)地將 256 個(gè)元素從 b 復(fù)制到 a,但是編譯器無法了解程序員的意圖。它必須假設(shè)最壞的情況,并且不能應(yīng)用需要在進(jìn)入循環(huán)之前提供行程數(shù)的優(yōu)化。此外,如果最終值是一個(gè)變量,您還應(yīng)該避免在循環(huán)條件中使用關(guān)系運(yùn)算符 <= 和 >=。如果循環(huán)條件是 i <= n,那么 n 有可能是該類型中可表示的最高值,因此編譯器必須假定這是一個(gè)潛在的無限循環(huán)。

別名

通常,我們不建議使用全局變量。這是因?yàn)槟稍诔绦虻娜魏蔚胤叫薷娜肿兞?,并且程序?huì)因全局變量的值而變化。這就會(huì)形成復(fù)雜的依賴關(guān)系,使人很難理解程序,也很難確定改變?nèi)肿兞康闹禃?huì)對(duì)程序產(chǎn)生怎樣的影響。從優(yōu)化器的角度來看,這種情況更糟糕,因?yàn)橥ㄟ^指針的存儲(chǔ)就可以改變?nèi)我馊肿兞康闹怠H绻芡ㄟ^多種方式訪問一個(gè)變量,這種情況就會(huì)被稱為別名,而別名使代碼更難優(yōu)化。

char *buf

void clear_buf()

int i;

for (i = 0; i < 128; ++i)

buf[i] = 0;

盡管程序員知道向 buf 所指向的緩存區(qū)進(jìn)行寫操作不會(huì)改變這個(gè)buf變量本身,但編譯器還是不得不做最壞的打算,在循環(huán)的每一次迭代中從內(nèi)存中重新加載 buf。

如果將緩存區(qū)的地址作為參數(shù)傳遞,而不是使用全局變量,則可以消除別名:

void clear_buf(char *buf)

int i;

for (i = 0; i < 128; ++i)

buf[i] = 0;

使用這個(gè)解決方案后,指針 buf 就不會(huì)被通過指針的存儲(chǔ)影響。如此一來,指針 buf 在循環(huán)中就可以保持不變,其值只需在循環(huán)前加載一次即可,而不是在每次迭代時(shí)都要重新加載。

然而,如果需要在不共享調(diào)用者/被調(diào)用者關(guān)系的代碼段之間傳遞信息,則直接使用全局變量即可。但是,對(duì)于計(jì)算密集型任務(wù),尤其是涉及指針操作時(shí),最好使用自動(dòng)變量。

盡量不用后增量和后減量

在下文中,關(guān)于后增量的所有內(nèi)容也適用于后減量。C 語言中關(guān)于后增量語義的標(biāo)準(zhǔn)文本指出:“后綴 ++ 運(yùn)算符的結(jié)果是操作數(shù)的值。在得到結(jié)果后,操作數(shù)的值會(huì)遞增”。雖然微控制器普遍擁有可在加載或存儲(chǔ)操作后增加指針的尋址模式,但其中只有很少能以同樣的效率處理其他類型的后增量。為符合標(biāo)準(zhǔn),編譯器必須在執(zhí)行增量之前將操作數(shù)復(fù)制到一個(gè)臨時(shí)變量。對(duì)于直線代碼來說,可以從表達(dá)式中取出增量,然后放在表達(dá)式之后。比如以下表達(dá)式:

foo = a[i++];

可以改為

foo = a[i];

i = i + 1;

但如果后增量屬于 while 循環(huán)中的條件,又會(huì)發(fā)生什么?由于在條件后面沒有可以插入增量的地方,因此必須在測試前添加增量。對(duì)于這些常見但是又與生成可執(zhí)行代碼效率密切相關(guān)的設(shè)計(jì),諸如IAR Systems的Embedded Workbench這樣的工具都在總結(jié)了大量實(shí)踐后提供了優(yōu)化方案。

比如以下循環(huán)

i = 0;

while (a[i++] ?。?0)

...

應(yīng)改為

loop:

temp = i; /* 保存操作數(shù)的值 */

i = temp + 1; /* 遞增操作數(shù) */

if (a[temp] == 0) /* 使用保存的值 */

goto no_loop;

...

goto loop;

no_loop:

loop:

temp = a[i]; /* 使用操作數(shù)的值 */

i = i + 1; /* 遞增操作數(shù) */

if (temp == 0)

goto no_loop;

...

goto loop;

no_loop:

如果循環(huán)后的 i 的值不相關(guān),最好將增量放在循環(huán)內(nèi)。比如以下幾乎相同的循環(huán)

i = 0;

while (a[i] ?。?0)

++i;

...

可以在沒有臨時(shí)變量的情況下執(zhí)行:

loop:

if (a[i] == 0)

goto no_loop;

i = i + 1;

...

goto loop;

no_loop:

優(yōu)化編譯器的開發(fā)者們很清楚后增量會(huì)使代碼編寫變得更復(fù)雜,盡管我們已盡力去識(shí)別這些模式,并盡量消除臨時(shí)變量,但總有一些情況使我們無法產(chǎn)生有效代碼,尤其是遇到比上述更復(fù)雜的循環(huán)條件時(shí)。通常,我們會(huì)將一個(gè)復(fù)雜的表達(dá)式分割成若干個(gè)更簡單的表達(dá)式,就像上面的循環(huán)條件被分割成一個(gè)測試和一個(gè)增量那樣。

在 C++ 環(huán)境中,選擇前增量還是后增量的重要性更高。這是因?yàn)?operator++ 和 operator-- 都可以以前綴和后綴的形式重載。將運(yùn)算符作為類對(duì)象重載時(shí),雖然沒必要模仿基本類型運(yùn)算符的行為,但也應(yīng)盡量接近。因此,對(duì)于那些可以直觀地對(duì)對(duì)象進(jìn)行遞增和遞減的類,例如迭代器,通常會(huì)有前綴(operator++() 和 operator--())和后綴形式(operator++(int) 和 operator--(int))。

為了模擬基本類型的前綴 ++ 的行為,operator++() 可以修改對(duì)象并返回對(duì)修改后對(duì)象的引用。那么模擬基本類型的后綴 ++ 的行為會(huì)怎樣?您還記得嗎?“后綴 ++ 運(yùn)算符的結(jié)果是操作數(shù)的值。在得到結(jié)果后,操作數(shù)的值會(huì)遞增”。就像上面的非直線代碼一樣,operator++(int) 的實(shí)現(xiàn)者必須復(fù)制原始對(duì)象,修改原始對(duì)象,并按值返回副本。由于存在復(fù)制操作,因此 operator++(int) 的開銷要高于 operator++()。

對(duì)于基本類型,如果忽略 i++ 的結(jié)果,優(yōu)化器通??梢韵槐匾膹?fù)制,但優(yōu)化器不能將對(duì)一個(gè)重載運(yùn)算符的調(diào)用變?yōu)榱硪粋€(gè)。如果您出于習(xí)慣編寫 i++ 而不是 ++i,您就會(huì)調(diào)用開銷更大的增量運(yùn)算符。

雖然我們一直在反對(duì)使用后增量,但不得不承認(rèn),后增量在有些情況下還是有用的。如果確實(shí)要給一個(gè)變量進(jìn)行后置增量操作,那就繼續(xù)吧。如果后增量操作和您期望的操作一致,可以使用后增量操作。但請(qǐng)注意,切勿為避免多寫一行代碼來遞增變量,而使用后增量操作。

每當(dāng)您在循環(huán)條件、if 條件、switch 表達(dá)式、?:- 表達(dá)式或函數(shù)調(diào)用參數(shù)中添加不必要的后增量時(shí),都會(huì)使編譯器不得不生成更大、更慢的代碼。這個(gè)清單是不是太長了,記不住?今天就開始培養(yǎng)好的習(xí)慣吧!在使用后增量操作前,先問問自己能不能把增量操作作為下一條語句。

結(jié)語

當(dāng)然,軟件開發(fā)工作并不是只要求開發(fā)人員去“將就”編譯器,他們與編譯器之間的相互協(xié)同是快速而高效地完成編程工作的基礎(chǔ)之一。此外,從編譯器的發(fā)展過程來看,它們不僅要跟隨技術(shù)和語言的演進(jìn)而迭代和創(chuàng)新,而且還要廣泛參考更多的開發(fā)習(xí)慣,那些歷史更悠久、使用更廣泛的編譯器可以為開發(fā)人員帶來更高的效率。

因此,在了解了如何編寫利于一款優(yōu)秀編譯器優(yōu)化的代碼之后,用戶們的工作效率就可以事半功倍。本文中提到的這些原理和tips,也是IAR Systems這樣的公司長時(shí)間總結(jié)的最優(yōu)實(shí)踐,而且都可以在該公司的Embedded Workbench中進(jìn)行驗(yàn)證和探索,在其工具界面中可以查看代碼的執(zhí)行時(shí)間和代碼尺寸,從而找到最佳解決方案。

pYYBAGJCvA-AVVW4AAGf-AAqHQU164.png

pYYBAGJCvBCAL8-BAABFj0m763w649.png

好的工具除了通用的代碼編譯優(yōu)化,還支持高度靈活的自定義優(yōu)化設(shè)置,如IAR Embedded Workbench包含針對(duì)運(yùn)行效率和代碼體積的不同優(yōu)化等級(jí),對(duì)于不同的應(yīng)用需求,還可以設(shè)置從整個(gè)工程,到每個(gè)源代碼文件,甚至是每個(gè)函數(shù)的優(yōu)化等級(jí),幫助工程師為自己的應(yīng)用適配出最佳的優(yōu)化方案。希望此篇文章對(duì)于開發(fā)人員更深度地了解程序優(yōu)化有所幫助。

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

    關(guān)注

    40

    文章

    3520

    瀏覽量

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

    關(guān)注

    1

    文章

    1602

    瀏覽量

    48896
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    ida反編譯出來代碼能直接用嗎

    IDA反編譯出來的代碼通常 不能直接使用 ,這主要基于以下幾個(gè)方面的原因: 一、代碼的不完整性 IDA反編譯生成的代碼可能缺少原始源
    的頭像 發(fā)表于 09-02 10:55 ?219次閱讀

    AI編譯器技術(shù)剖析

    隨著人工智能技術(shù)的飛速發(fā)展,AI編譯器作為一種新興的編譯技術(shù)逐漸進(jìn)入人們的視野。AI編譯器不僅具備傳統(tǒng)編譯器的功能,如將高級(jí)語言編寫的源
    的頭像 發(fā)表于 07-17 18:28 ?1211次閱讀

    人工智能編譯器與傳統(tǒng)編譯器的區(qū)別

    人工智能編譯器(AI編譯器)與傳統(tǒng)編譯器在多個(gè)方面存在顯著的差異。這些差異主要體現(xiàn)在設(shè)計(jì)目標(biāo)、功能特性、優(yōu)化策略、適用范圍以及技術(shù)復(fù)雜性等方面。以下是對(duì)兩者區(qū)別的詳細(xì)探討,旨在全面解析
    的頭像 發(fā)表于 07-17 18:19 ?1265次閱讀

    Meta發(fā)布基于Code Llama的LLM編譯器

    近日,科技巨頭Meta在其X平臺(tái)上正式宣布推出了一款革命性的LLM編譯器,這一模型家族基于Meta Code Llama構(gòu)建,并融合了先進(jìn)的代碼優(yōu)化編譯器功能。LLM
    的頭像 發(fā)表于 06-29 17:54 ?1310次閱讀

    芯來科技與華東師范大學(xué)SOLE實(shí)驗(yàn)室合作推動(dòng)LLVM/CLANG編譯器優(yōu)化

    行深入的LLVM/CLANG編譯器優(yōu)化以及程序性能優(yōu)化和調(diào)優(yōu)。 我們不僅優(yōu)化了LLVM編譯器的多個(gè)關(guān)鍵環(huán)節(jié),提升了
    的頭像 發(fā)表于 06-12 09:09 ?465次閱讀
    芯來科技與華東師范大學(xué)SOLE實(shí)驗(yàn)室合作推動(dòng)LLVM/CLANG<b class='flag-5'>編譯器</b><b class='flag-5'>優(yōu)化</b>

    SEGGER編譯器優(yōu)化和安全技術(shù)介紹 支持最新C和C++語言

    代碼生成,SEGGER編譯器生成非常小的代碼,非常適合內(nèi)存受限的環(huán)境,而不會(huì)犧牲執(zhí)行速度。 2)?速度優(yōu)化:在最高優(yōu)化級(jí)別,SEGGER
    的頭像 發(fā)表于 06-04 15:31 ?1168次閱讀
    SEGGER<b class='flag-5'>編譯器</b><b class='flag-5'>優(yōu)化</b>和安全技術(shù)介紹 支持最新C和C++語言

    KEIL怎么禁止某個(gè)函數(shù)/某段代碼編譯器優(yōu)化?

    有沒有大佬知道,KEIL怎么禁止某個(gè)函數(shù)/某段代碼編譯器優(yōu)化?
    發(fā)表于 04-10 08:17

    英特爾宣布DPC++編譯器優(yōu)化無效,2600項(xiàng)基準(zhǔn)測試成績丟失

    根據(jù)公告,該編譯器有選擇性地運(yùn)用了與SPEC代碼及數(shù)據(jù)集的先驗(yàn)知識(shí)有關(guān)的轉(zhuǎn)換,僅致力于提升523.xalancbmk_r/623.xalancbmk_s基準(zhǔn)性能,而這種優(yōu)化形式的適用范圍相對(duì)狹窄。
    的頭像 發(fā)表于 02-19 13:53 ?426次閱讀

    QT開發(fā)學(xué)習(xí)筆記1(安裝交叉編譯器

    QT安裝交叉編譯器
    的頭像 發(fā)表于 02-18 10:02 ?688次閱讀
    QT開發(fā)學(xué)習(xí)筆記1(安裝交叉<b class='flag-5'>編譯器</b>)

    Triton編譯器的原理和性能

    Triton是一種用于編寫高效自定義深度學(xué)習(xí)原語的語言和編譯器。Triton的目的是提供一個(gè)開源環(huán)境,以比CUDA更高的生產(chǎn)力編寫快速代碼,但也比其他現(xiàn)有DSL具有更大的靈活性。Tri
    的頭像 發(fā)表于 12-16 11:22 ?2219次閱讀
    Triton<b class='flag-5'>編譯器</b>的原理和性能

    TVM編譯器的整體架構(gòu)和基本方法

    。但是這其中也去思考了一下基于FPGA加速編譯器架構(gòu)。在FPGA深度學(xué)習(xí)加速中,編譯器除了需要自動(dòng)化生成指令外,還要優(yōu)化指令的結(jié)構(gòu),來
    的頭像 發(fā)表于 11-30 09:36 ?1863次閱讀
    TVM<b class='flag-5'>編譯器</b>的整體架構(gòu)和基本方法

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

    一個(gè)程序首先要保證正確性,在保證正確性的基礎(chǔ)上,性能也是一個(gè)重要的考量。要編寫高性能的程序,第一,必須選擇合適的算法和數(shù)據(jù)結(jié)構(gòu);第二,應(yīng)該編寫編譯器能夠有效優(yōu)化以轉(zhuǎn)換成高效可執(zhí)行
    的頭像 發(fā)表于 11-24 15:37 ?753次閱讀
    <b class='flag-5'>編譯器</b>的<b class='flag-5'>優(yōu)化</b>選項(xiàng)

    CPU程序幾個(gè)優(yōu)化程序性能的手段詳解

    要寫出高性能的代碼,首先需要對(duì)編譯器有基礎(chǔ)的了解,原因在于現(xiàn)代編譯器有很強(qiáng)的優(yōu)化能力,但有些代碼編譯器
    的頭像 發(fā)表于 11-21 09:46 ?449次閱讀
    CPU程序幾個(gè)<b class='flag-5'>優(yōu)化</b>程序性能的手段詳解

    開發(fā)單片機(jī)下位機(jī)需要對(duì)編譯器很了解嗎?

    如題所說,單片機(jī)下位機(jī)程序編寫,需要對(duì)編譯器很了解嗎。 對(duì)單片機(jī)這種程序來說,都有哪些編譯器編譯代碼。
    發(fā)表于 10-23 06:22

    新版編譯器的設(shè)計(jì)思路和優(yōu)化方法

    小程序編譯器在小程序開發(fā)、預(yù)覽、發(fā)布各個(gè)階段都需要使用,因此編譯器性能會(huì)直接影響到開發(fā)者開發(fā)效率,也會(huì)影響到開發(fā)者工具的使用體驗(yàn)。 由于舊版的編譯器(基于 webpack4)在構(gòu)建大型項(xiàng)目時(shí)會(huì)很慢,內(nèi)存占用也高,一直被開發(fā)者吐槽
    發(fā)表于 10-13 11:21 ?256次閱讀
    新版<b class='flag-5'>編譯器</b>的設(shè)計(jì)思路和<b class='flag-5'>優(yōu)化</b>方法