C#中那些[舉手之勞]的效能優化

發表於2014-04-04

隔了很久沒寫東西了,主要是最近比較忙,更主要的是最近比較懶……

其實這篇很早就想寫了

工作和生活中經常可以看到一些程式猿,寫程式碼的時候只關注程式碼的邏輯性,而不考慮執行效率

其實這對大多數程式猿來說都是沒有問題的

不過作為一隻有理想的CodeMonkey,我還是希望給大家分享一些效能優化心得

曾經在網上聽過這樣一句話

程式的可讀性和效能是成反比的

我非常贊同這句話,所以對於那些極度影響閱讀的效能優化我就不在這裡贅述了

今天主要說的就是一些舉手之勞即可完成的效能優化

 減少重複程式碼

這是最基本的優化方案,儘可能減少那些重複做的事,讓他們只做一次

比較常見是這種程式碼,同樣的Math.Cos(angle) 和Math.Sin(angle)都做了2次

優化後

還有另一種 ,在方法中例項化一個物件, 但是這個物件其實是可以複用的

優化後

還有一種是不必要的初始化,比如呼叫out引數之前,是不需要初始化的

這裡的new User()就是不必要的操作,

優化後

不要迷信正規表示式

正好在第一個栗子裡說到了正在表示式(Regex)物件就順便一起說了

很多人以為正規表示式很快,非常快,超級的快

雖然正規表示式是挺快的,不過千萬不要迷信他,不信你看下面的栗子

有多少人認為正規表示式比較快的,舉個手??

rgwerg2314rvgerger25338811040

結果為10w次迴圈的時間 ,即使是10個Replace連用,也比Regex好,所以不要迷信他

ConvertQuot1:3518
ConvertQuot2:12479

最後給你們看一個真實的,杯具的栗子

合理使用正規表示式

上面說了正規表示式的效率不高,並不是說就不要用他了,至少正規表示式的作用不僅僅如此而已

如果一定要用正規表示式的話也需要注意,能靜態全域性公用的儘量全域性公用

注意他的第二個引數RegexOptions.Compiled 註釋是  指定將正規表示式編譯為程式集。這會產生更快的執行速度,但會增加啟動時間。

通俗的說就是加了這個列舉,會使得初始化Regex物件變慢,但是執行字串查詢的時候更快, 不使用的話,初始化很多,查詢比較慢

之前測過相差蠻大的 ,程式碼就不比較了,有興趣的可以自己試試相差多少

另外還有一些列舉項,不確定是否對效能有影響,不過還是按規則使用會比較好

  • RegexOptions.IgnoreCase    // 指定不區分大小寫的匹配,  如果表示式中沒有字母,則不需要設定
  • RegexOptions.Multiline         // 多行模式。更改 ^ 和 $ 的含義….  如果表示式中沒有^和$,則不需要設定
  • RegexOptions.Singleline       // 指定單行模式。更改點 (.) 的含義….  如果表示式中沒有.,則不需要設定

讓編譯器預處理常量的計算

編譯器在編譯程式段的時候 如果發現有一些運算是常量對常量的,那麼他會在編譯期間就計算完成,這樣可以使程式在執行時不用重複計算了

比如

ewfwefwef231449524591669

不過編譯器有的時候也不是那麼聰明的

ergegege231453256152482

這個時候就需要我們幫助一下了

yhrhr231453426932297

給他加一個括號,讓他知道應該先計算常量,這樣就可以在編譯期間進行運算了

字串比較

這個可能很多人知道了,但還是提一下

1,2最慢 3較快 4,5最快

1,2幾乎沒區別 4,5幾乎沒區別

不過這個只適用於比較null和空字串,如果是連續的空白就是string.IsNullOrWhiteSpace最快了,不過這個方法2.0裡面沒有

所以2.0可以這樣 (s+””).trim() == 0

這裡的關鍵就是 s + “”  這個操作可以把null轉換為””

注意第二個引數只能是””或string.Empty 這樣的累加幾乎是不消耗時間的,如果第二個引數是” “(一個空格)這個時間就遠遠不止了

字串拼接

字串累加,這個道理和Regex一樣,不要盲目崇拜StringBuilder

在大量(或不確定的)string拼接的時候,StringBuilder確實可以起到提速的作用

而少數幾個固定的string累加的時候就不需要StringBuilder 了,畢竟StringBuilder 的初始化也是需要時間的

感謝殘蛹 博友提供的說明

ps: 這段我確實記得我是寫過的來著,不知道怎麼的,發出來的時候就不見了…..

rghergerer231515097401697

此外還有一個string.Concat方法,該方法可以小幅度的優化程式的速度,幅度很小

他和string.Join的區別在於沒有間隔符號(我之前常用string.Join(“”,a,b,c,d),不要告訴我只有我一個人這麼幹)

另一種經常遇到的字串拼接

對於這種情況有2中優化的方案

對於3.5以上可以直接使用Linq輔助,這種方案程式碼少,但是效能相對差一些

對於非3.5或對效能要求極高的場合

bool型別的判斷返回

這種現象常見於新手程式設計師中

型別的判斷

一般型別的判斷有2種形式

1,這種屬於程式碼比較好寫,但是效能比較低, 原因就是GetType()的時候消耗了很多時間

2,這種屬性寫程式碼麻煩,但是效能很高的型別

其實有個中間之道,既可以保證效能又可以比較好寫

大部分情況下 這個是可以用的 如果你自己有個型別實現了IConvertible,然後返回TypeCode.Int32 就不再這個討論範圍之內了

使用列舉作為索引

下面這個是一個真實的例子,為了突出重點,做了部分修改,刪除了多餘的分支,原始碼中不只4個

優化後

不過有的時候,列舉不一定都是連續的數字,那麼也可以使用Dictionary

這種優化在分支比較多的時候很好用,少的時候作用有限

字元型別Char,分支判斷時的處理技巧

這部分內容比較複雜,而且適用範圍有限,如果平時用不到的就可以忽略了

在處理字串物件的時候,有時會需要判斷char的值然後做進一步的操作

這裡有一種空間換時間的優化方式, 雖說是空間換時間,但是實際浪費的空間不會很多,因為char最多隻有65536長度

原先僅特殊符號一部分就需要判斷12次,修改過後只判斷一次就可以得到結果了

這方面的栗子在我的Json元件(程式碼)(文章1,2,3)中也有使用

 摘取部分blqw.Json的程式碼

結束了…還要後續嗎?…貌似我又要懶一段時間

我寫的文章,除了純程式碼,其他的都是想表達一種思想,一種解決方案.希望各位看官不要侷限於文章中的現成的程式碼,要多關注整個文章的主題思路,謝謝!

相關文章