C 效能優化指南

2022-06-12 05:18:10 字數 3139 閱讀 7214

優化是一項編碼活動。在傳統的軟體開發過程中,直到編碼完成,專案進入了整合與測試階段,能夠觀察到程式整體的效能時,才會進行優化。而在敏捷開發方式中,當乙個帶有效能指標的特性編碼完成後或是需要實現特定的效能目標時,就會分配乙個或多個衝刺 (sprint)進行優化。

效能優化的目的是通過改善正確程式的行為使其滿足客戶對處理速度、吞吐量、記憶體占用以及能耗等各種指標的需求。因此,效能優化與編碼對開發過程而言有著同等的重要性。對於使用者而言,效能糟糕得讓人無法接受,這個問題的嚴重程度不亞於出現 bug 和未實現的特性。

bug 修復與效能優化之間的乙個重要區別是,效能是乙個連續變數。特性要麼是實現了,要麼是沒有實現;bug 要麼存在,要麼不存在。但是效能可以是非常糟糕或者非常優秀,還可能是介於這二者之間的某種程度。優化還是乙個迭代的過程。每當程式中最慢的部分被改善後,乙個新的最慢的部分就會出現。

與其他編碼任務相比,優化更像是一門實驗科學,需要有更深入的科學思維方式。要想優化成功,需要先觀察程式行為,然後基於這些程式行為作出可測試的推測,再進行實驗得出測試結果。這些測試結果要麼驗證推測,要麼推翻推測。經驗豐富的開發人員常常相信他們對於最優**具有足夠的經驗和直覺。但是除非他們頻繁地測試自己的直覺,否則通常他們都是錯誤的。在我個人為本書編寫測試**的經歷中,就多次出現測試結果與我的直覺相悖的情況。實驗,而非直覺,才是貫穿本書的主題。

開發人員很難理解單個編碼決策對大型程式的整體效能的影響。因此,實際上所有完整的程式都包含大量的優化機會。即使是經驗豐富的團隊在時間充裕的情況下編寫出的**,執行速度通常也可以提高 30% 至 100%。我見過對在時間很緊張的情況下或是欠缺經驗的團隊編寫出的**進行優化後,程式執行速度提高了 3 至 10 倍的情況。不過,通過微調**讓程式的執行速度提公升 10 多倍幾乎是不可能的。但是選擇一種更好的演算法或是資料結構,則可以將程式的某個特性的效能從慢到無法忍受提公升至可發布的狀態。

許多關於效能優化的討論都會首先嚴正地警告大家:「不要!不要優化!如果確實需要,那麼請在專案結束時再優化,而且不要做任何非必需的優化。」例如,著名的計算機科學家高德納曾經這樣說過:

我們應當忘記小的效能改善,百分之九十七的情況下,過早優化都是萬惡之源。

——高德納,structured programming with go to statements ,acm computing

「不要進行優化」這條建議已經成為了一項程式設計常識,甚至許多經驗豐富的程式設計師都認為這是毋庸置疑的。他們對效能調優避而不談。我認為過度推崇這條建議經常被用作兩種行為的藉口:程式設計惡習,以及逃避做少量分析以讓**執行得更快。同時我還認為,盲目地接受這條建議會導致浪費大量 cpu 週期、使用者滿意度下降,會浪費大量時間重寫那些本應從一開始就更加高效的**。

我的建議是不要過於教條。優化是沒有問題的。學習高效的程式設計慣用法並在程式中實踐之是沒有問題的,即使你不知道哪部分**的效能很重要。這些慣用法對 c++ 程式很有幫助。使用這些技巧也不會讓你被同事鄙夷。如果有人問你為什麼不寫一些「簡單」和低效的**,你可以這麼回答他:「編寫高效**與編寫低效無用的**所需的時間是一樣的,為什麼還會有人特意去編寫低效的**呢?」

但是,如果你不清楚重要性,因為不確定哪個演算法更好而導致許多天過去了專案仍毫無進展,這是不行的。你因為猜測某段**有嚴格的執行時間的要求,就花費數週時間去編寫彙編**,然後將**作為函式被呼叫(而實際上 c++ 編譯器可能已經將函式內聯展開了),這是不行的。當你實際上並不知道 c 是否真的更快以及 c++ 是否真的不快時,僅僅因為「大家都知道 c 更快」,就要求你的團隊在 c++ 程式中使用 c 語言編寫部分**,這是不行的。換言之,所有軟體開發的最佳實踐依然都是適用的。優化並不能成為打破這些規則的理由。

不知道效能問題出在**就花費很多時間進行優化,這是不行的。在第 3 章中我將介紹「90/10 規則」。這條規則指出程式中只有 10% 的**的效能是很重要的。因此,試圖修改程式中的每條語句去改善程式效能沒有必要,也不會有作用。既然只有 10% 的**會對程式的效能產生顯著的影響,那麼試圖隨機找出乙個效能改善切入點的概率就會很低。第 3 章會講解如何使用一些工具幫助大家定位**中的「熱點」。

當我還在讀大學時,我的教授曾經警告我們,最優演算法的啟動效能開銷可能比簡單演算法更大,因此,應當只在大型資料集上使用它們。可能對於某些冷僻的演算法來說確實如此,但根據我的經驗,對於簡單的查詢和排序任務而言,最優演算法的準備時間很少,即使在小型資料集上使用它們也能改善效能。

我也曾經被建議在開發程式時隨便使用一種最容易實現的演算法,之後在發現程式執行得太慢時,再回過頭來優化它。不可否認,這對於推進專案持續進展是一條好的建議,但是一旦你已經編寫過幾次最優查詢或排序的演算法了,那麼與編寫低效的演算法相比,編寫最優演算法並不會更難。而且你也可以在初次編寫演算法時就正確地實現並除錯一種演算法,這樣以後就可以復用它了。

實際上,常識可能是效能改善最大的敵人。例如,「每個人都知道」最優排序演算法的時間複雜度是 o (n log n ),其中 n 是資料集的大小(參見 5.1 節中關於大 o 符號和時間開銷的簡單回顧)。這條常識非常有價值,甚至讓開發人員不相信他們的 o (n 2 ) 插入排序(insertion sort)演算法是最優的,但如果它阻止了開發人員查閱文獻得出以下發現就不好了:基數排序(radix sort)演算法的時間複雜度是 o (n log r n )(其中 r 是基數或用於排序的桶的數量),處理速度更快;對於隨機分布的資料,flashsort 演算法的時間複雜度是 o (n ),處理速度更快;還有快速排序演算法,根據常識人們將它作為測量其他排序演算法效能的測試基準,但在最壞的情況下,它的時間複雜度是 o (n 2 ) 。

亞里斯多德曾經誤認為「女人的牙齒比男人少」,這個公認的觀點讓人們信奉了 1500 年,直到有人非常好奇,數了幾張嘴中的牙齒數。常識的「解毒劑」是實驗形式的科學方法。在第 3 章中我們將利用工具測量軟體效能,並通過實驗驗證優化效果。

在軟體開發的世界中,還有一條常識:優化是不重要的。這條常識的理由是,儘管現在**執行得很慢,但是每年都會有更快的處理器被研發出來,隨著時間的推移,它們會免費幫你解決效能問題。就像大多數常識一樣,這種想法完全錯誤。在 20 世紀 80 年代和 90 年代,這種想法似乎看起來是正確的。那時,桌面電腦和獨立的應用程式占領了軟體開發領域,而且單核處理器的處理速度每 18 個月就翻一倍。雖然總的看來,如今多核處理器的效能不斷強大,但是單個核心的效能增長卻非常緩慢,甚至有時還有所下降。如今的程式還必須執行於移動平台上,電池的電量和散熱都制約了指令的執行速率。而且,儘管隨著時間的推移,會給新客戶帶來更快的計算機,但是也無法改善現有硬體的效能。現有客戶的工作負載隨著時間的推移在不斷地增加。你的公司為現有客戶提高處理速度的唯一辦法是發布效能優化後的新版本。優化可以讓程式永遠保持活力。

C 效能優化指南

1 用好的編譯器並用好編譯器 支援c 11的編譯器,intelc 速度最快 gnu的c 編譯器gcc g 非常符合標準 visual c 效能折中 clang 最年輕mac os x 2 使用更好的演算法。3 使用更好的資料結構 不同的資料結構在使用記憶體管理器的方式也有所不同 4 使用更好的庫 熟...

jQuery效能優化指南

這個話題也是老生常談的了,jquery倒是沒少用,卻很少考慮它的效能!隨著cpu的嗷嗷上揚,覺得應該好好考慮這個問題!最近讀到e文 jquery performance rules 拿來跟大家分享,粗略的翻譯了一下!1 always descend from an id 總是從id選擇器開始繼承 2...

Elasticsearch 效能優化指南

本場 chat 將從 4 個層面 系統 集群 索引 api 介紹 elasticsearch.優化方案,分別包括寫入的優化和查詢的優化。共包含優化細項 50 餘項。總結了與多位大咖的交流經驗,在此分享給大家。我會針對優化原理做一些簡單的解釋,以及利弊權衡。方便讀者針對實際的業務場景進行針對性優化。本...