如何讓編譯器不要優化某段代碼
遇到的問題是想重新編譯某個java文件(比如A.java),裡面有個常量(比如finalinta)和上次編譯時不一樣,但是另一個使用A.class的a的文件(比如B.java)由於在javac在上次編譯的時候將當時的A.class裡面的常量直接給內聯了,所以就達不到想要的效果。
如果是這樣的話,對於String可以使用.intern()來防止編譯器進行優化,對於其他類型,可以要麼不定義為常量,要麼將常量定義為private,然後使用一個static方法來返回這個常量。
⑵ 現代C/C++編譯器有多智能
最近在搞C/C++代碼的性能優化,發現很多時候自以為的優化其實編譯器早就優化過了,得結合反匯編才能看出到底要做什麼樣的優化。
請熟悉編譯器的同學結合操作系統和硬體談一談現代c/c++編譯器到底有多智能吧。哪些書本上的優化方法其實早就過時了?
以及程序員做什麼會讓編譯器能更好的自動優化代碼?
舉個栗子:
1,循環展開,大部分編譯器設置flag後會自動展開;
2,順序SIMD優化,大部分編譯器設置flag後也會自動優化成SIMD指令;
3,減少中間變數,大部分編譯器會自動優化掉中間變數;
etc.
查看代碼對應的匯編:
Compiler Explorer
【以下解答】
舉個之前看過的例子:
int calc_hash(signed char *s){ static const int N = 100003; int ret = 1; while (*s) { ret = ret * 131 + *s; ++ s; } ret %= N; if (ret < 0) ret += N; //注意這句 return ret;}
【以下解答】
舉個簡單例子,一到一百求和
#include int sum() { int ret= 0; int i; for(i = 1; i <= 100; i++) ret+=i; return ret;}int main() { printf("%d\n", sum()); return 0;}
【以下解答】
話題太大,碼字花時間…
先放傳送門好了。
請看Google的C++編譯器組老大Chandler Carruth的演講。這個演講是從編譯器研發工程師的角度出發,以Clang/LLVM編譯C++為例,向一般C++程序員介紹理解編譯器優化的思維模型。它講解了C++編譯器會做的一些常見優化,而不會深入到LLVM具體是如何實現這些優化的,所以即使不懂編譯原理的C++程序員看這個演講也不會有壓力。
Understanding Compiler Optimization - Chandler Carruth - Opening Keynote Meeting C++ 2015
演示稿:https://meetingcpp.com/tl_files/mcpp/2015/talks/meetingcxx_2015-understanding_compiler_optimization_themed_.pdf
錄像:https://www.youtube.com/watch?v=FnGCDLhaxKU(打不開請自備工具…)
Agner Fog寫的優化手冊也永遠是值得參考的文檔。其中的C++優化手冊:
Optimizing software in C++ - An optimization guide for Windows, Linux and Mac platforms - Agner Fog
要稍微深入一點的話,GCC和LLVM的文檔其實都對各自的內部實現有不錯的介紹。
GCC:GNU Compiler Collection (GCC) Internals
LLVM:LLVM』s Analysis and Transform Passes
========================================
反模式(anti-patterns)
1. 為了「優化」而減少源碼中局部變數的個數
這可能是最沒用的手工「優化」了。特別是遇到在高級語言中「不用臨時變數來交換兩個變數」這種場景的時候。
看另一個問題有感:有什麼像a=a+b;b=a-b;a=a-b;這樣的演算法或者知識? - 編程
2. 為了「優化」而把應該傳值的參數改為傳引用
(待續…)
【以下解答】
推薦讀一讀這里的幾個文檔:
Software optimization resources. C++ and assembly. Windows, Linux, BSD, Mac OS X
其中第一篇:http://www.agner.org/optimize/optimizing_cpp.pdf
講解了C++不同領域的優化思路和問題,還有編譯器做了哪些優化,以及如何代碼配合編譯器優化。還有優化多線程、使用向量指令等的介紹,推薦看看。
感覺比較符合你的部分需求。
【以下解答】
一份比較老的slides:
http://www.fefe.de/source-code-optimization.pdf
【以下解答】
利用C++11的range-based for loop語法可以實現類似python里的range生成器,也就是實現一個range對象,使得
for(auto i : range(start, stop, step))
【以下解答】
我覺得都不用現代。。。。寄存器分配和指令調度最智能了
【以下解答】
每次編譯poco庫的時候我都覺得很為難GCC
【以下解答】
有些智能並不能保證代碼變換前後語義是等價的
【以下解答】
誒誒,我錯了各位,GCC是可以藉助 SSE 的 xmm 寄存器進行優化的,經 @RednaxelaFX 才知道應該添加 -march=native 選項。我以前不了解 -march 選項,去研究下再來補充為什麼加和不加區別這么大。
十分抱歉黑錯了。。。以後再找別的點來黑。
誤導大家了,實在抱歉。(??ˇ?ˇ??)
/*********以下是並不正確的原答案*********/
我是來黑 GCC的。
最近在搞編譯器相關的活,編譯OpenSSL的時候有一段這樣的代碼:
BN_ULONG a0,a1,a2,a3; // EmmetZC 註:BN_ULONG 其實就是 unsigned longa0=B[0]; a1=B[1]; a2=B[2]; a3=B[3];A[0]=a0; A[1]=a1; A[2]=a2; A[3]=a3;
【以下解答】
提示:找不到對象
【以下解答】
忍不住抖個機靈。
私以為正常寫代碼情況下編譯器就能優化,才叫智能編譯器。要程序員絞盡腦汁去考慮怎麼寫代碼能讓編譯器更好優化,甚至降低了可讀性,那就沒有起到透明屏蔽的作用。
智能編譯器應該是程序猿要較勁腦汁才能讓編譯器不優化。
理論上是這樣的。折疊我吧。
【以下解答】
編譯器智能到每次我都覺得自己很智障。
【以下解答】
雖然題主內容里是想問編譯器代碼性能優化方面的內容,但題目里既然說到編譯器的的智能,我就偏一下方向來說吧。
有什麼更能展示編譯器的強大和智能?
自然是c++的模版元編程
template meta programming
簡單解釋的話就是寫代碼的代碼,寫的還是c++,但能讓編譯器在編譯期間生成正常的c++代碼。
沒接觸過的話,是不是聽上去感覺就是宏替換的加強版?感覺不到它的強大呢?
只是簡單用的話,效果上這樣理解也沒什麼
但是一旦深入下去,尤其翻看大神寫的東西,這明明看著就是c++的代碼,但TM怎麼完全看不懂他在干什麼?後來才知道這其實完全是另外一個世界,可是明明是另外一個世界的東西但它又可以用來做很多正常c++能做的事....
什麼?你說它好像不能做這個,不能做那個,好像做不了太多東西,錯了,大錯特錯。就像你和高手考試都考了100分的故事一樣,雖然分數一樣,但你是努力努力再努力才得了滿分,而高手只是因為卷面分只有100分.....在元編程面前,只有想不到,沒有做不到。
再回頭看看其他答案,編譯器順手幫你求個和,丟棄下無用代碼,就已經被驚呼強大了,那模板元編程這種幾乎能在編譯期直接幫你「生成」包含復雜邏輯的c++代碼,甚至還能間接「執行」一些復雜邏輯,這樣的編譯器是不是算怪獸級的強大?
一個編譯器同時支持編譯語法相似但結果不同卻又關聯的兩種依賴語言,這個編譯器有多強大多智能?
寫的人思維都要轉換幾次,編譯器轉著圈嵌著套翻著番兒地編譯代碼的代碼也肯定是無比蛋疼的,你說它有多強大多智能?
一個代碼創造另外一個代碼,自己能按照相似的規則生成自己,是不是聽上去已經有人工智慧的發展趨勢了?
上帝說,要有光,於是有了光。
老子曰,一生二,二生三,三生萬物。
信c++,得永生!
===
FBI WARNING:模板元編程雖然很強大,但也有不少缺點,尤其對於大型項目,為了你以及身邊同事的身心健康,請務必適度且謹慎的使用。勿亂入坑,回頭是岸。
【以下解答】
c++11的auto自動類型推斷算么....
【以下解答】
智能到開不同級別的優化,程序行為會不同 2333
【以下解答】
這個取決於你的水平
⑶ Keil優化及如何設置某段代碼不優化
Keil默認會對代碼進行優化的
一般情況預設編譯優化設置被設定為8級優化,實際最高可設定為9級優化。
1、Dead code elimination
2、Data overlaying
3、Peephole optimization
4、Register variables
5、Common subexpression elimination
6、Loop rotation
7、Extended Index Access Optimizing
8、Reuse Common Entry Code
9、Common Block Subroutines
如何設定不優化或改變某個文件的優化設置
1、針對某個文件
(1) 右鍵->Option for File XXXX
(2) 切換到【C51】選項卡
default表示與工程設置一致
2、針對某段代碼(局部優化)
#pragma OPTIMIZE(x) x就是你希望的優化級別
舉例如下:
#pragma OPTIMIZE(6)
void FunA()
{
}
......
......
#pragma OPTIMIZE(9)
void FunB()
{
}
上面的意思就是說,在void FunA()到void FunB()之前的所有函數,包括FunA在內,都採用6級的優化,而從FunB開始直到之後,只要沒碰上#pragma OPTIMIZE,都採用9級優化了。
OPTIMIZE還可以多一個參數,就是speed和size,
用法: #pragma OPTIMIZE(9,speed)或#pragma OPTIMIZE(5,size)
對應的就是9級優化,以速度為主,或5級優化,以空間最小為主。
⑷ 下段代碼會被頻繁調用 怎麼優化使其更適合運行
1. 記住阿姆達爾定律:
funccost是函數func運行時間百分比,funcspeep是你優化函數的運行的系數。
所以,如果你優化了函數TriangleIntersect執行40%的運行時間,使它運行快了近兩倍,而你的程序會運行快25%。
這意味著不經常使用的代碼不需要做較多優化考慮(或者完全不優化)。
這里有句俗語:讓經常執行的路徑運行更加高效,而運行稀少的路徑正確運行。
2. 代碼先保證正確,然後再考慮優化
這並不意味著用8周時間寫一個全功能的射線追蹤演算法,然後用8周時間去優化它。
分多步來做性能優化。
先寫正確的代碼,當你意識到這個函數可能會被經常調用,進行明顯的優化。
然後再尋找演算法的瓶頸,並解決(通過優化或者改進演算法)。通常,改進演算法能顯著地改進瓶頸——也許是採用一個你還沒有預想到的方法。所有頻繁調用的函數,都需要優化。
3. 我所了解的那些寫出非常高效代碼的人說,他們優化代碼的時間,是寫代碼時間的兩倍。
4.跳轉和分支執行代價高,如果可能,盡量少用。
函數調用需要兩次跳轉,外加棧內存操作。
優先使用迭代而不是遞歸。
使用內聯函數處理短小的函數來消除函數調用開銷。
將循環內的函數調用移動到循環外(例如,將for(i=0;i<100;i++) DoSomething();改為DoSomething() { for(i=0;i<100;i++) { … }})。
if…else if…else if…else if…很長的分支鏈執行到最後的分支需要很多的跳轉。如果可能,將其轉換為一個switch聲明語句,編譯器有時候會將其轉換為一個表查詢單次跳轉。如果switch聲明不可行,將最常見的場景放在if分支鏈的最前面。
5. 仔細思考函數下標的順序。
兩階或更高階的數組在內存中還是以一維的方式在存儲在內存中,這意味著(對於C/C++數組)array[i][j] 和 array[i][j+1]是相鄰的,但是array[i][j] 和array[i+1][j]可能相距很遠。
以適當的方式訪問存儲實際內存中的數據,可以顯著地提升你代碼的執行效率(有時候可以提升一個數量級甚至更多)。
現代處理器從主內存中載入數據到處理器cache,會載入比單個值更多的數據。該操作會獲取請求數據和相鄰數據(一個cache行大小)的整塊數據。這意味著,一旦array[i][j]已經在處理器cache中,array[i][j+1]很大可能也已經在cache中了,而array[i+1][j]可能還在內存中。
⑸ 怎麼指定某段代碼不被編譯器優化掉
在C語言中, 某些語句,如:
int a;
a = 0;
a = 1;
a =2; 這個可能編譯器會把前面兩句給優惠掉, 這個如果 前面兩句也是必須要執行的, 可以把 int a 改成 volatile int a。
在編譯的時候, 編譯器可能會預測到某個變數的值, 就把中間的沒有必要的語句給優化掉,volatile 關鍵字就是告訴編譯器,不要做這樣的預測性優化, 按照文本代碼來翻譯。
⑹ 求助怎樣讓編譯器只優化部分代碼
編譯器,是將便於人編寫,閱讀,維護的高級計算機語言翻譯為計算機能解讀、運行的低階機器語言的程序。編譯器將原始程序(Source program)作為輸入,翻譯產生使用目標語言(Target language)的等價程序。源代碼一般為高階語言 (High-level language), 如 Pascal、C++、Java 等,而目標語言則是匯編語言或目標機器的目標代碼(Object code),有時也稱作機器代碼(Machine code)。
一個現代編譯器的主要工作流程如下:
源代碼 (source code) → 預處理器 (preprocessor) → 編譯器 (compiler) → 匯編程序 (assembler) → 目標代碼 (object code) → 連接器 (Linker) → 可執行程序 (executables)!
⑺ Debug模式下怎樣去掉編譯器的優化解決思路
通常開發的程序有2種模式:Debug模式和Release模式。
在Debug模式下,編譯器會記錄很多調試信息,也可以加入很多測試代碼,方便程序員測試,以及出現bug時的分析解決。
Release模式下,就沒有上述那些調試信息,而且編譯器也會自動優化一些代碼,這樣生成的程序性能是最優的,但是如果出現問題,就不方便分析測試了。
⑻ 如何優化你的C代碼
一、程序結構的優化
1、程序的書寫結構
雖然書寫格式並不會影響生成的代碼質量,但是在實際編寫程序時還是應該尊循一定的書寫規則,一個書寫清晰、明了的程序,有利於以後的維護。在書寫程序時,特別是對於While、for、do…while、if…elst、switch…case等語句或這些語句嵌套組合時,應採用「縮格」的書寫形式,
2、標識符
程序中使用的用戶標識符除要遵循標識符的命名規則以外,一般不要用代數符號(如a、b、x1、y1)作為變數名,應選取具有相關含義的英文單詞(或縮寫)或漢語拼音作為標識符,以增加程序的可讀性,如:count、number1、red、work等。
3、程序結構
C語言是一種高級程序設計語言,提供了十分完備的規范化流程式控制制結構。因此在採用C語言設計單片機應用系統程序時,首先要注意盡可能採用結構化的程序設計方法,這樣可使整個應用系統程序結構清晰,便於調試和維護。於一個較大的應用程序,通常將整個程序按功能分成若干個模塊,不同模塊完成不同的功能。各個模塊可以分別編寫,甚至還可以由不同的程序員編寫,一般單個模塊完成的功能較為簡單,設計和調試也相對容易一些。在C語言中,一個函數就可以認為是一個模塊。所謂程序模塊化,不僅是要將整個程序劃分成若干個功能模塊,更重要的是,還應該注意保持各個模塊之間變數的相對獨立性,即保持模塊的獨立性,盡量少使用全局變數等。對於一些常用的功能模塊,還可以封裝為一個應用程序庫,以便需要時可以直接調用。但是在使用模塊化時,如果將模塊分成太細太小,又會導致程序的執行效率變低(進入和退出一個函數時保護和恢復寄存器佔用了一些時間)。
4、定義常數
在程序化設計過程中,對於經常使用的一些常數,如果將它直接寫到程序中去,一旦常數的數值發生變化,就必須逐個找出程序中所有的常數,並逐一進行修改,這樣必然會降低程序的可維護性。因此,應盡量當採用預處理命令方式來定義常數,而且還可以避免輸入錯誤。
5、減少判斷語句
能夠使用條件編譯(ifdef)的地方就使用條件編譯而不使用if語句,有利於減少編譯生成的代碼的長度,能夠不用判斷語句則少用判斷用語句。
6、表達式
對於一個表達式中各種運算執行的優先順序不太明確或容易混淆的地方,應當採用圓括弧明確指定它們的優先順序。一個表達式通常不能寫得太復雜,如果表達式太復雜,時間久了以後,自己也不容易看得懂,不利於以後的維護。
7、函數
對於程序中的函數,在使用之前,應對函數的類型進行說明,對函數類型的說明必須保證它與原來定義的函數類型一致,對於沒有參數和沒有返回值類型的函數應加上「void」說明。如果果需要縮短代碼的長度,可以將程序中一些公共的程序段定義為函數,在Keil中的高級別優化就是這樣的。如果需要縮短程序的執行時間,在程序調試結束後,將部分函數用宏定義來代替。注意,應該在程序調試結束後再定義宏,因為大多數編譯系統在宏展開之後才會報錯,這樣會增加排錯的難度。
8、盡量少用全局變數,多用局部變數。
因為全局變數是放在數據存儲器中,定義一個全局變數,MCU就少一個可以利用的數據存儲器空間,如果定義了太多的全局變數,會導致編譯器無足夠的內存可以分配。而局部變數大多定位於MCU內部的寄存器中,在絕大多數MCU中,使用寄存器操作速度比數據存儲器快,指令也更多更靈活,有利於生成質量更高的代碼,而且局部變數所的佔用的寄存器和數據存儲器在不同的模塊中可以重復利用。
9、設定合適的編譯程序選項
許多編譯程序有幾種不同的優化選項,在使用前應理解各優化選項的含義,然後選用最合適的一種優化方式。通常情況下一旦選用最高級優化,編譯程序會近乎病態地追求代碼優化,可能會影響程序的正確性,導致程序運行出錯。因此應熟悉所使用的編譯器,應知道哪些參數在優化時會受到影響,哪些參數不會受到影響。
在ICCAVR中,有「Default」和「Enable Code Compression」兩個優化選項。
在CodeVisionAVR中,「Tiny」和「small」兩種內存模式。
在IAR中,共有7種不同的內存模式選項。
在GCCAVR中優化選項更多,一不小心更容易選到不恰當的選項。
二、代碼的優化
1、選擇合適的演算法和數據結構
應該熟悉演算法語言,知道各種演算法的優缺點,具體資料請參見相應的參考資料,有很多計算機書籍上都有介紹。將比較慢的順序查找法用較快的二分查找或亂序查找法代替,插入排序或冒泡排序法用快速排序、合並排序或根排序代替,都可以大大提高程序執行的效率。.選擇一種合適的數據結構也很重要,比如你在一堆隨機存放的數中使用了大量的插入和刪除指令,那使用鏈表要快得多。
數組與指針語句具有十分密碼的關系,一般來說,指針比較靈活簡潔,而數組則比較直觀,容易理解。對於大部分的編譯器,使用指針比使用數組生成的代碼更短,執行效率更高。但是在Keil中則相反,使用數組比使用的指針生成的代碼更短。。
3、使用盡量小的數據類型
能夠使用字元型(char)定義的變數,就不要使用整型(int)變數來定義;能夠使用整型變數定義的變數就不要用長整型(long int),能不使用浮點型(float)變數就不要使用浮點型變數。當然,在定義變數後不要超過變數的作用范圍,如果超過變數的范圍賦值,C編譯器並不報錯,但程序運行結果卻錯了,而且這樣的錯誤很難發現。
在ICCAVR中,可以在Options中設定使用printf參數,盡量使用基本型參數(%c、%d、%x、%X、%u和%s格式說明符),少用長整型參數(%ld、%lu、%lx和%lX格式說明符),至於浮點型的參數(%f)則盡量不要使用,其它C編譯器也一樣。在其它條件不變的情況下,使用%f參數,會使生成的代碼的數量增加很多,執行速度降低。
4、使用自加、自減指令
通常使用自加、自減指令和復合賦值表達式(如a-=1及a+=1等)都能夠生成高質量的程序代碼,編譯器通常都能夠生成inc和dec之類的指令,而使用a=a+1或a=a-1之類的指令,有很多C編譯器都會生成二到三個位元組的指令。在AVR單片適用的ICCAVR、GCCAVR、IAR等C編譯器以上幾種書寫方式生成的代碼是一樣的,也能夠生成高質量的inc和dec之類的的代碼。
5、減少運算的強度
可以使用運算量小但功能相同的表達式替換原來復雜的的表達式。如下:
(1)、求余運算。
a=a%8;
可以改為:
a=a&7;
說明:位操作只需一個指令周期即可完成,而大部分的C編譯器的「%」運算均是調用子程序來完成,代碼長、執行速度慢。通常,只要求是求2n方的余數,均可使用位操作的方法來代替。
(2)、平方運算
a=pow(a,2.0);
可以改為:
a=a*a;
說明:在有內置硬體乘法器的單片機中(如51系列),乘法運算比求平方運算快得多,因為浮點數的求平方是通過調用子程序來實現的,在自帶硬體乘法器的AVR單片機中,如ATMega163中,乘法運算只需2個時鍾周期就可以完成。既使是在沒有內置硬體乘法器的AVR單片機中,乘法運算的子程序比平方運算的子程序代碼短,執行速度快。
如果是求3次方,如:
a=pow(a,3.0);
更改為:
a=a*a*a;
則效率的改善更明顯。
(3)、用移位實現乘除法運算
a=a*4;
b=b/4;
可以改為:
a=a<<2;
b=b>>2;
說明:通常如果需要乘以或除以2n,都可以用移位的方法代替。在ICCAVR中,如果乘以2n,都可以生成左移的代碼,而乘以其它的整數或除以任何數,均調用乘除法子程序。用移位的方法得到代碼比調用乘除法子程序生成的代碼效率高。實際上,只要是乘以或除以一個整數,均可以用移位的方法得到結果,如:
a=a*9
可以改為:
a=(a<<3)+a
6、循環
(1)、循環語
對於一些不需要循環變數參加運算的任務可以把它們放到循環外面,這里的任務包括表達式、函數的調用、指針運算、數組訪問等,應該將沒有必要執行多次的操作全部集合在一起,放到一個init的初始化程序中進行。
(2)、延時函數:
通常使用的延時函數均採用自加的形式:
void delay (void)
{
unsigned int i;
for (i=0;i<1000;i++)
;
}
將其改為自減延時函數:
void delay (void)
{
unsigned int i;
for (i=1000;i>0;i--)
;
}
兩個函數的延時效果相似,但幾乎所有的C編譯對後一種函數生成的代碼均比前一種代碼少1~3個位元組,因為幾乎所有的MCU均有為0轉移的指令,採用後一種方式能夠生成這類指令。
在使用while循環時也一樣,使用自減指令控制循環會比使用自加指令控制循環生成的代碼更少1~3個字母。
但是在循環中有通過循環變數「i」讀寫數組的指令時,使用預減循環時有可能使數組超界,要引起注意。
(3)while循環和do…while循環
用while循環時有以下兩種循環形式:
unsigned int i;
i=0;
while (i<1000)
{
i++;
//用戶程序
}
或:
unsigned int i;
i=1000;
do
i--;
//用戶程序
while (i>0);
在這兩種循環中,使用do…while循環編譯後生成的代碼的長度短於while循環。
7、查表
在程序中一般不進行非常復雜的運算,如浮點數的乘除及開方等,以及一些復雜的數學模型的插補運算,對這些即消耗時間又消費資源的運算,應盡量使用查表的方式,並且將數據表置於程序存儲區。如果直接生成所需的表比較困難,也盡量在啟動時先計算,然後在數據存儲器中生成所需的表,後以在程序運行直接查表就可以了,減少了程序執行過程中重復計算的工作量。
⑼ 老師說的那個bug如果不優化代碼怎麼解決
C語言屬於編譯語言,也就是你編寫的程序,要經過編譯形成目標代碼,具體的處理器才能執行這個程序。 C語言的編譯器有多種演算法,如代碼長度最孝代碼執行時間最短等等。你在開發環境中不對代碼優化進行設置,那就是默認等級,或者叫無優化。
⑽ 如何防止編譯器優化特定函數
在51環境下:options for target -> C51 -> emphasis選擇default (不要選擇size、speed)
優化標准:選擇 0:constant folding 即可保留所有垃圾語句