題:
在開發和調試階段禁用優化真的是一個好習慣嗎?
Daniel Grillo
2010-08-06 23:44:21 UTC
view on stackexchange narkive permalink

我已經閱讀了用C語言編程16位PIC單片機,並且在書中有這樣的肯定:

在a的開發和調試階段不過,在項目中,最好禁用所有優化,因為它們可能會修改所分析代碼的結構,並導致單步執行和斷點放置問題。

我承認我是一個有點困惑。我不知道作者是因為 C30評估期還是因為這是一種很好的做法。

我想知道您是否真的使用了這種做法,為什麼?

十二 答案:
Michael Kohne
2010-08-07 00:10:54 UTC
view on stackexchange narkive permalink

這在整個軟件工程中都是很標準的-當您優化代碼時,只要您不知道操作上的任何區別,編譯器就可以按照自己的意願重新安排很多事情。因此,例如,如果您在循環的每次迭代內初始化變量,而從不在循環內更改變量,則允許優化器將該初始化移出循環,以免浪費時間。

它還可能會意識到您計算了一個數字,然後您在覆蓋之前不做任何事情。在那種情況下,它可能會消除無用的計算。

優化的問題是,您需要在一些代碼上放置一個斷點,優化器已將其移動或刪除。在這種情況下,調試器將無法執行您想要的操作(通常,它將把斷點放置在附近)。因此,為使生成的代碼與您編寫的代碼更相似,請在調試過程中關閉優化-這可以確保您要中斷的代碼確實存在。

您需要對此小心謹慎,但是,根據您的代碼,優化可能會破壞事情!通常,被正確運行的優化器破壞的代碼實際上只是無法解決問題的錯誤代碼,因此您通常想弄清楚為什麼優化器會破壞它。

與該論證相反的是,優化器可能會使事情變得更小和/或更快,如果您的代碼受時間或大小的限制,那麼您可能會通過禁用優化來破壞某些東西,並浪費時間調試沒有解決問題的程序。真的存在。當然,調試器可能會使您的代碼變慢並且變大。
在哪裡可以了解更多信息?
我尚未使用C30編譯器,但對於C18編譯器,有一份應用說明/該編譯器手冊涵蓋了它支持的優化。
@O Engenheiro:檢查您的編譯器文檔以了解其支持哪些優化。優化取決於編譯器,庫和目標體系結構而千差萬別。
同樣,不是針對C30編譯器,而是gcc發布了可以應用的各種優化的長列表。如果您要保留完整的特定控件結構,也可以使用此列表進行細粒度的優化。列表在這裡:http://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html
@Reemrevnivek,我將禁用單個文件優化,該優化具有影響力和總體時序,但隨後使用該文件對其他系統進行基準測試。進行所有優化後,我將使用LED閃爍,並使用顯微鏡對其進行測量以測量時序。
-1
Daniel Grillo
2010-11-03 23:04:24 UTC
view on stackexchange narkive permalink

我已將這個問題發送給傑克·甘斯爾(Jack Ganssle),這就是他的回答:

丹尼爾,

我更喜歡使用調試中的任何優化進行調試發布的代碼。美國宇航局說:“測試你的飛行,測試你的飛行。”換句話說,不要先測試然後更改代碼!

但是,有時必須關閉優化程序才能使調試器正常工作。我試圖在我正在處理的模塊中將其關閉。出於這個原因,我相信保持文件較小,說幾百行代碼左右。

最好,傑克

我認為此回應中的兩個段落之間存在未說明的區別。測試是指應該證明軟件,公司和/或硬件正常工作的過程。調試是一個過程,在該過程中,代碼逐個指令逐步執行,以查看其為何仍無法運行。
有選擇的感覺真好。因此,無論是否進行優化,測試都可以涵蓋更多的品種/排列。覆蓋範圍越大,測試越好
@reemrevnivek,調試時,您也要測試嗎?
@O Engenheiro-不。我僅在測試失敗的情況下進行調試。
Mark
2010-08-07 00:22:10 UTC
view on stackexchange narkive permalink

取決於,通常所有工具(不僅是C30)都是如此。

優化通常以各種方式刪除和/或重組代碼。您的switch語句可能使用if / else構造重新實現,或者在某些情況下可能一起被刪除。 y = x * 16可能會替換為一系列左移,等等。儘管最後一種優化通常仍可以逐步執行,但其主要是對獲得ya的控制語句的重組。

由於您在C中定義的結構不再存在,它們被編譯器替換或重新排序為編譯器認為會更快或更快速地編譯的代碼,因此可能無法通過C代碼逐步調試程序使用更少的空間。這也可能導致無法從C列表中設置斷點,因為斷點的指令可能不再存在。例如,您可以嘗試在if語句中設置一個斷點,但是編譯器可能已刪除了該if。您可以嘗試在while或for循環中設置一個斷點,但是編譯器決定展開該循環,以使其不再存在。您應該始終在進行優化的情況下進行重新測試。這是您發現錯過重要的 volatile 及其導致間歇性故障(或其他怪異現象)的唯一方法。

對於嵌入式開發,無論如何,您必須謹慎進行優化。特別是在對時間要求嚴格的代碼段中,例如一些中斷。在這種情況下,您應該對彙編中的關鍵位進行編碼,或者使用編譯器指令來確保未對這些部分進行優化,以使您知道它們具有固定的執行時間或固定的最壞情況運行時間。

另一個難題可能是使代碼適合uC,您可能需要代碼密度優化才能將代碼簡單地適合芯片。這就是為什麼通常一個好主意是從一個家庭中最大的ROM容量uC開始,然後在代碼被鎖定後才選擇一個較小的ROM進行製造。

semaj
2010-08-07 01:18:46 UTC
view on stackexchange narkive permalink

通常,我將使用計劃發布的任何設置進行調試。如果要發布優化的代碼,則可以使用優化的代碼進行調試。如果要發布未優化的代碼,則將使用未優化的代碼進行調試。我這樣做有兩個原因。首先,優化器可以產生足夠大的時序差異,從而導致最終產品的行為與未優化的代碼不同。其次,儘管大多數都不錯,但是編譯器供應商確實會犯錯誤,並且優化的代碼可能會與未優化的代碼產生不同的結果。因此,無論我打算發布什麼設置,我都希望獲得盡可能多的測試時間。如果我發現很難調試的特定代碼部分,則將暫時關閉優化器,進行調試以使代碼正常工作,然後重新打開優化器並再次進行測試。

但是,只要啟用優化,單步執行代碼幾乎是不可能的。關閉優化進行調試,然後使用發布代碼運行單元測試。
mikeselectricstuff
2010-11-04 05:02:55 UTC
view on stackexchange narkive permalink

我通常的策略是進行最終的優化(適當時,選擇最大的大小或速度),但是如果我需要調試其他跟踪信息,則暫時關閉優化。

一個典型的故障模式是,當您由於沒有在必要時將變量聲明為易失性而使優化增加導致以前看不見的錯誤浮出水面時,這是典型的故障模式。告訴編譯器哪些東西不應該“優化”是至關重要的。

Grady Player
2013-01-27 07:31:14 UTC
view on stackexchange narkive permalink

在斷點的情況下肯定是有道理的...因為編譯器可以刪除很多實際上不影響內存的語句。

請考慮以下內容:

  int i = 0;對於(int j = 0; j < 10; j ++){i + = j;}返回0;  

可以完全優化(因為 i 從未被讀取)。從斷點的角度來看,它基本上跳過了所有代碼,甚至根本不在那兒……。我想這就是為什麼在睡眠類型函數中您經常會看到類似這樣的原因:

  for(int j = delay; j!= 0; j-){asm(“ nop”); asm(“ nop”);}返回0;  
old_timer
2010-08-07 08:06:20 UTC
view on stackexchange narkive permalink

使用將要發布的任何形式,調試器和用於調試的編譯會隱藏很多(很多)直到編譯發布才可見的錯誤。到那時,要找到這些錯誤要比進行調試要困難得多。到現在已有20多年了,我再也沒有使用過gdb或其他類似調試器的東西,不需要監視變量或單步執行。每天數百到數千行。

編譯為調試,然後再編譯為發布可能會花費兩倍甚至兩倍以上的精力。如果您陷入困境並且必須使用調試器之類的工具,然後進行編譯以使調試器解決特定問題,然後恢復為正常運行。

其他問題也同樣存在,例如優化器使代碼速度更快,因此,特別是對於嵌入式而言,您的時序更改會帶有編譯器選項,並且可能會影響程序的功能,因此在此處再次使用整個階段的可交付編譯選項。編譯器也是程序,有錯誤,優化器會出錯,有些人對此不抱任何信念。如果是這種情況,那麼不進行優化就可以編譯,沒錯,只要一直這樣做就可以了。我更喜歡的路徑是進行編譯以進行優化,然後,如果我懷疑編譯器問題禁用了優化(如果解決了該問題),通常會來回往復,有時會檢查彙編器輸出以找出原因。

+1只是為了詳細說明您的好答案:經常以“調試”模式進行編譯會在未分配空間的變量周圍填充堆棧/堆,從而減輕了較小的筆誤和格式化字符串錯誤。如果在發行版中進行編譯,您通常會遇到良好的運行時崩潰。
akohlsmith
2010-08-07 08:27:13 UTC
view on stackexchange narkive permalink

我總是使用-O0(gcc選項關閉優化功能)開發代碼。當我感覺到我想開始讓事情更趨於發佈時,我將從-Os(針對大小進行優化)開始,因為通常可以在緩存中保留的代碼越多越好,我發現gdb與-O0代碼一起使用時效果更好,如果您必須進入程序集,則遵循起來會容易得多。

在-O0和-Os之間切換還可讓您查看編譯器對代碼的作用。有時候這是很有趣的教育,它還可以發現編譯器錯誤...那些使您費力地試圖弄清楚代碼出什麼問題的討厭的東西!

如果我真的需要,我將開始在帶有-gc-sections的-fdata-sections和-fcode-sections中添加,這使鏈接器可以刪除實際上不使用的全部功能和數據段。您可以修補很多小東西,以嘗試進一步縮小或加快速度,但總的來說,這是我最終使用的唯一技巧,而任何較小或更快的東西我都會處理-組裝。

davidcary
2010-08-12 20:12:49 UTC
view on stackexchange narkive permalink

是的,暫時禁用調試期間的優化一直是最佳實踐,原因有三點:

  • (a),如果您要單步執行程序,
  • (a)(已作廢),如果您要使用彙編語言調試器對程序進行單步調試,則混亂程度將大大降低。 (但是,當您可以使用高級調試器時,為什麼還要為此煩惱呢?)
  • (b)(早已過時),您可能只會運行一次該特定可執行文件,然後創建一些可執行文件更改並重新編譯。當編譯器“優化”此特定的可執行文件時,這將浪費一個人的時間,而這將節省不到10分鐘的運行時間。 (這與現代PC可以在不到2秒的時間內完全優化地編譯典型的微控制器可執行文件無關。)

許多人朝著這個方向走得更遠,並且打開斷言的船

單步執行彙編代碼對於診斷源代碼實際指定的內容與實際情況有所不同的情況非常有用(例如“ longvar1&=〜0x40000000; longvar2&=〜0x80000000;”)或編譯器生成錯誤代碼的情況。我已經使用機器代碼調試器找到了一些問題,我真的認為我不可能以其他任何方式找到問題。
stevenvh
2011-07-16 12:59:33 UTC
view on stackexchange narkive permalink

簡單:優化是費時的,如果以後在開發中必須更改那段代碼,可能沒有用。因此,它們很可能浪費時間和金錢。
它們對於完成的模塊很有用;代碼中最有可能不需要更改的部分。

mjh2007
2010-08-07 01:51:22 UTC
view on stackexchange narkive permalink

如果您使用的是調試器,那麼我會禁用優化功能並啟用調試。

我個人發現PIC調試器會導致更多問題,而無法解決。
我只是使用printf()到USART調試用C18編寫的程序。

Morten Jensen
2013-06-14 22:22:15 UTC
view on stackexchange narkive permalink

大多數反對在編譯中啟用優化的論點歸結為:

  1. 調試困難(JTAG連接,斷點等)
  2. 錯誤的軟件計時
  3. >
  4. sh * t不能正常工作
  5. ol>

    恕我直言,前兩個是合法的,第三個不是很多。這通常意味著您有一些錯誤的代碼,或者依賴於對語言/實現的不安全利用,或者作者只是對Undefined Undefined Behaviour的忠實擁護者。一兩個關於未定義行為的說法,這篇文章是關於編譯器如何利用它的: http://blog.regehr.org/archives/761

另一個可能的原因是,打開優化後,編譯器可能會很煩人。


該問答將自動從英語翻譯而來。原始內容可在stackexchange上找到,我們感謝它分發的cc by-sa 2.0許可。
Loading...