咱們在上一篇博客中設定了架構的目標,只有一個,就是可維護性。徹底沒有提性能,這是故意的。html
彷佛程序員都是急性子,或許是被windows冗長的開機時間折磨夠了,有多是由於提高性能的效果是最顯而易見的……總之,我發現,絕大部分程序員對性能的關注和熱情是無與倫比的!git
因此直到今天,我仍然看到不少程序員無怨無悔的用存儲過程來構建他們的系統,一個存儲過程能夠有幾千行!而後,他們很無辜的問,「業務層有什麼用?究竟能幹些什麼呢?」程序員
在帶團隊的時候,我最怕講的就是性能有關的問題。你要是不談性能呢,那代碼有時候真心看不下去;你要是強調性能呢,不知道他會給你整出什麼幺蛾子出來。其實這就是一個「度」的掌握,因此很是難以用語言予以表示清楚。因此無數次挫敗以後,我只好咬牙切齒的說,「你的代碼,只有一個評判標準,可維護性。性能的問題先無論!」這個答案彷佛並不能服衆——尤爲是對有上進心的程序員而言。面試
因此,我先專篇講性能,但願能幫助你們更清楚的認識這個問題。算法
1、性能不是不重要,而是他沒有可維護性重要。要理解這一點,首先要理解可維護性的重要(請再讀上一篇我花數週找bug的段子);而後要明白:解決性能問題,咱們能夠有不少代碼之外行之有效的方法,而可維護性基本上就只能靠代碼了;最後,仍是要牢記:沒有犧牲,就沒有勝利!sql
2、因此,在絕大多數狀況下,當性能和可維護性相沖突的時候,性能讓位於可維護性。咱們採用其餘辦法來彌補代碼性能不夠高的問題。數據庫
空洞的說教沒有意義。咱們仍是舉例來講明吧!編程
前段時間我review代碼的時候發現,這個程序員用Linq以後總是用First()而不是Single(),我就奇怪了,按業務邏輯,返回的值就應該是一個,難道可能會是多個,多個應報異常,不該該取First()就完事了呀?想了一下子,問這個程序員,他的回答讓我瞬間一種無力感,「First()性能更高呀!」如下爲對話實錄:windows
「你怎麼知道First()性能更高呢?」我問。緩存
「First()嘛,取了第一個合格的值就返回,就不會繼續查下去了;Single()的話,就會一直查,查出全部數據,而後再取其中的一個。」
「你肯定?你知道有一種東西叫作索引不?」
「啊?……」
而後我簡單的告訴他,索引是一種樹狀結構,可讓查詢更快等等。
「但我仍是以爲應該用First()」,他想了一下子,仍是很堅決。
「爲何?」,我不明白了。
「就算有索引加快了查詢速度,但用First()在加快了速度上更快呀!更快老是沒錯的吧?」
「……」,我真不知道該怎麼說了,最後忽然靈光一閃,「好吧,那你說說,微軟爲何要搞一個Single()方法出來呢?就爲了搞出來誤導大家?讓用First()的產生優越感,嘲笑用Single()的?」
他陷入了沉思。
評論裏還在糾結Single()/First()的同窗,請大聲的吼三遍:可讀性!可讀性!!可讀性!!!
發現同窗們還在糾結這個細節。好吧,再解釋一下:
一、你怎麼知道數據庫用的就是MSSQL呢?你怎麼知道就是用的關係數據庫呢?NoSQL不行麼?因此,你怎麼就知道Single()/First()具體是怎麼執行的呢?好比我就要寫個Linq實現,把全部的數據全取出來,而後再在內存裏排序,最後取First呢?
二、這裏咱們考慮可讀性,意思是:讀代碼時,看到Single()就能瞬間知道coder的意思是取惟一的一個;看到First()就知道coder的意思是要取第一個。和性能不要緊,若是必定要糾纏性能,那好:你要肯定惟一性,固然要作檢查(包括不惟一時拋異常),這個性能損失是應該的呀;你要取第一個,固然要進行排序,排序也會有性能損失呀!
我剛入行的時候,還非常收藏了幾篇文章,好比《高性能編程的十大準則》之類的,裏面的內容大體就是,「老是使用StringBuilder,不要使用‘+’;老是使用……,不要使用……」。這類文章下面老是有一堆人叫好,「不錯!」,「謝謝分享!」但慢慢的,我就對這些文章產生了懷疑(也應該感謝園子裏的老趙,csdn裏面的sp1234之類的大神);直到很後來,我才明白爲何這種說法是膚淺的;而只有經過上面的對話,我才能清晰的把個人理解說出來。
全部這些犧牲性能的簡單封裝,都是有其目的的;而其中一個很重要的目的,就是爲了提升可讀性。你爲了性能,故意不使用這些現成的封裝,一般,喪失的就是可讀性。
繼續上面這個例子。最開始的時候,這個程序員關於性能的考慮實際上是想固然的。這種想固然的情形不少,大體有這幾種:
第一、2種比較好理解,第3種爲何也說他「想固然」呢?由於沒有和硬件環境相契合。
最簡單的例子就是「緩存」。好比面試的時候,問你一個問題,「緩存能不能提升性能?」請注意,這是一個陷阱。答案應該是:「不必定」。幾乎全部的人都認爲,緩存能夠迅速改善性能,是由於今天計算機的CPU和磁盤運行速度,遠跟不上內存的發展。但即便如此,無節制的緩存,同樣能夠拖垮整個系統。
相似的例子還有不少。你沾沾自喜,我節約了一次磁盤讀寫的時候,你同時增長了CPU的負荷;你優化了算法,減小了CPU的運算,但其實增長了內存的壓力……天下沒有免費的午飯。一樣的代碼,隨着數據的增長,硬件的改變,會呈現出大相徑庭的性能表現。
因此,開發過程當中,不少的「優化」,其實只是你的想固然。與其這樣想固然的優化,不如在拿到性能測試結果以後再有的放矢的進行優化。這時候,又回到了咱們以前說的,是否是代碼的可讀性更重要?這樣你才能迅速的找到該優化的瓶頸啊!不然,一堆亂七八糟看都看不懂的代碼,你怎麼去優化,你連該優化的點都找不到。
另外一個搞笑的例子是關於我本身的。創業家園項目裏有一個功能:顯示博客正文的同時提供一個上一頁下一頁的連接。慣常的作法就是直接在數據庫裏查就是了,但我總以爲不對,這樣作兩次查詢有必要麼?能不能優化?因而我想到了一個「絕妙」的點子:爲何不直接在博客裏存儲上一篇和下一篇的Id呢?這樣我一次性數據往返就能取到全部數據了嘛!各位同窗是否是以爲我這個主意很棒?
噩夢由此開始了。
首先,咱們是想在發佈博客的時候,設置他的上一篇和下一篇。可是,上一篇好設置,下一篇呢?尚未啊!怎麼弄,就只好在博客發佈的時候,設置他的前一篇,同時設置他前一篇的後一篇。
而後,咱們新添加了一個功能,除了上一篇下一篇之外,還須要在當前博客所在分類中的上一篇和下一篇。怎麼辦?再加字段唄。因此,博客裏就有了Previous, PreviousInCategory, Next, NextInCategory。這時候,就感受到有點不妥,但還能夠接受。
接着,出現了一個問題,上一篇下一篇博客被刪除了,怎麼辦?這個過程,就至關於從一個雙向鏈表裏移出一個節點同樣麻煩。頭開始有點大了。
再接着,博客除了發佈刪除之外,還有各類其餘狀態,好比被屏蔽。並且被屏蔽以後,可否顯示和當前用戶又有關係。當前用戶是普通用戶,不能閱讀;當前用戶是做者本身,就可以閱讀。怎麼辦?首先,屏蔽的時候,要設置上一篇下一篇;屏蔽取消的時候,仍是要設置上一篇下一篇。而後,上一篇下一篇得根據當前用戶不一樣變化的這個問題,基本上就傻眼了……
最後流着淚把辛辛苦苦折騰了很久的代碼全改回來,就經過數據庫查唄,多麼清晰簡潔的邏輯啊!性能問題?首先,這樣作形成了性能問題麼?而後,就算有問題,用一個緩存能解決不?
說了這麼多,不知道有沒有引發同窗們的反思。可能你們仍是過不去內心那道坎:明明有一種性能更高的方法咱們爲何不用?
由於浪費唄!
什麼?你有沒有搞錯?個人代碼,至少省了一塊內存條!那是你還沒從「窮學生」的角色裏轉換過來。你花一週的時間對代碼進行了優化(就先不考慮你的優化帶來的維護成本增長了),爲老闆省下了一塊內存條的錢。你覺得老闆會拍着你的肩膀表揚你麼?老闆打不死你!
兄弟,帳不是你那樣算的。當你是學生的時候,你的時間成本是0;但你進入工做崗位,每一天都是要發工資的。
經過代碼來調高性能,是一種無奈——對硬件性能不夠的妥協(參考:80年代遊戲開發者的辛苦困境。這樣寫性能就高,但爲何如今沒有誰再這麼寫代碼了?)。不然,絕大多數狀況下,堆硬件比優化代碼的效果好得多,並且便宜得多。硬件的成本按摩爾定律往降低,咱們程序員的工資也能按摩爾定律減麼?
明明window 10 比window 95更耗性能,爲何今天沒人用window 95?爲何VS 2013要10G的空間咱們都還屁顛屁顛的趕忙裝上?爲何如今你們都用C#,沒人用匯編?咱們站在人類文明積累的今天,就應該理所固然的享受這一切成果。有打火機你不用,你要鑽木取火。若是你是由於要學貝爺荒野求生裝逼,能夠理解;若是你說你是由於怕浪費自然氣,我……我……我怎麼說你呢?「給作打火機的一條活路,行不?」一樣的,程序員大神同窗,你就當作好事,給下面寫底層作硬件的一條活路吧!你的代碼都是010001000010000001010101……了,你讓其餘人怎麼活啊?
最後,我忽然想到的一個程序員爲何對性能如此敏感瘋狂,對可維護性滿不在乎的一個可能緣由:
你們以爲是否是這樣的?因此,願意把代碼百鍊成鋼繞指柔的人少。想來,是一種莫名的悲哀和淒涼。
最後最後,有一些我能想到的名言警句供你們參詳:
忘了說個人項目了。目前主要集中在創業家園項目的開發上,正試圖從svn轉成git源代碼控制。不太懂Git,提及來都是淚,懂的同窗幫幫忙吧!