從程序員的角度去看爲什麼現在的電腦應用程序這麼喫內存(RAM)?

文章聲明,本篇文章題材源自知乎的問答 爲什麼現在的電腦應用程序這麼喫內存(RAM)?


 

別看程序員平時一個個自黑,黑得很嗨。但是當真正面對自己工作中的黑點時,大家都不提了。一個個都辯解說:內存不用就是浪費——但問題是,在大多數場景下,那些內存裏存的數據,恐怕大都是0。

大多數程序使用內存暴漲的鍋,就是現在大多數程序員的水平、責任心、工作環境造成的。

 

我舉一些例子:

  1. 現在很多程序員,在實際工作中,已經幾乎沒有精細化設計數據結構的能力了。例如說,有多少人在最近兩三年的工作中,使用到了bitfield?哪怕有用到的,大多也是因爲要沿用一些已經設計好的底層古老協議而不得不用。當然,位操作這個鍋主要是C/C++程序員背的。但是其他程序員也不見得好到哪去:有多少程序員自己在生產項目中,設計過一個除哈希表和鏈表外,其他任意一款數據結構及其變形結構的?說得難聽點,大多數腳本系語言的程序員,實際生產中,連基本的二叉樹之類都沒用過。
  2. 太依賴於哈希表+雙鏈表這種包打天下的組合了。我知道,這個組合確實很簡單很快捷。但是缺點就是本問題:內存消耗大。如果說腳本語言的程序員,確實有時候沒別的現成的數據結構可用,情有可原的話,那麼最近幾年,不少C/C++/JAVA的程序員,也逐漸染上了這個毛病。
  3. 文本化協議氾濫。傳統上,數據協議都是二進制的。對於計算機來說,二進制是最友好而且也冗餘最小效率最高的協議。但是因爲對人的識別度不好,尤其是web包打天下的潮流開始,現在很多系統,無論是網絡協議還是存儲協議,都基本上是文本化(http/html/json/xml……)的了。最後一個比較流行而且設計比較精巧的二進制協議,我覺得就是protocol buffers了。而且,這個鍋是會傳染的,一旦某個系統協議用了文本化之後,所有接入這個系統的協議,都必須都弄一套這樣的文本解析模塊。
  4. 代碼腳本化/動態化語言氾濫(包括解釋器機制的泛濫)。這個鍋主要來自於解釋器引擎,因爲在腳本語言裏,定義一個變量,基本上都不會出現char/short這類節約內存數據類型的,最最起碼都是上來就給你個int。而且這個int佔用的內存實際上往往還不僅僅是4個字節,而是這樣的:
union DATA
{
    int bool_val;
    intptr_t int_val;
    double double_val;
    char* str_val;
    XXX* obj_val;
};

可以看到,64位系統中,很多腳本解釋器裏,一個變量最最起碼佔用的也是一個指針的寬度(8個字節)。如果有額外的引用計數、動態類型標識符之類的話,還要再往上加。

 

最後一條:GC。這點大家都知道,我就不廢話了。

 

所以,很多答案都說了:瀏覽器很喫內存。回頭看看,瀏覽器裏跑的東西,命中了上面幾條?

能不喫內存嗎?


隨便補充幾條在評論中大量反覆出現的辯解來回復一下:

1:在代碼設計時就考慮了數據結構/內存佈局,並不意味着大量的額外工作量。

事實上,得益於各種庫/擴展,大部分常見的數據結構,都有現成的模塊可用。實際上,大多數情況下,數據結構/數據模型都是會單獨封裝/抽象出一個庫或者模塊的。所以,實際工作量無非也就是調A庫還是調B庫,可能也就內部接口api換了換而已,少數情況下這些api差異可能會少量穿透到功能代碼中。

至於說沒有合理抽象分層封裝的項目,所有功能代碼、數據操作代碼、還有其他的什麼亂七八糟的代碼都混在一起?小項目無所謂,就那三五條槍,說改就改,不改也無所謂,反正說不定什麼時候就死了。大項目?早死早超生吧。

2:寫出高質量的代碼並不代表項目研發速度降低——尤其當你的項目爛到一定程度需要大面積重構的話,把這些重構的時間和人力分開計算到平時的每個需求迭代中,只要有心做好,代碼都不至於被評價爲“爛”。大部分985211科班出來的程序員,這點能力我還是相信有的。

3:項目代碼爛,說真的,主要還是在沒有設計規劃時,爲了一昧的趕進度而反覆堆砌功能代碼,尤其是到處的代碼拷貝。事實上,在接需求動手前,設計和規劃代碼的工作,並不會耽誤開發進度,而且能讓代碼質量大幅提高——這在我經歷過的很多項目中都能體現出來。

4:至於bitfield這種東東,我也沒讓各位在項目中到處用——我也反對過早優化。在很多時候,能把pb用好就很不錯了。就說一個現實的例子,在某項目的mc,由原來各種亂七八糟的文本數據往裏丟,統一改爲pb格式後,同樣的機器數目,緩存數據條數增加了30%以上。光是這一改就減少了多少cache miss,減少了多少後端數據庫的壓力?

5:實際上,程序/指令/資源/棧等佔用的內存是很有限的。大多數內存,都是用於cache的——無非是誰用,用來幹什麼而已。相比於訪問cache數據的那點增刪改查的時間,cache miss導致訪問主存(文件/網絡/數據庫)纔是消耗的大頭。所以,內存佈局設計得好,合理使用數據結構,空間利用率高,在大多數時候直接體現就是訪問主存的頻度降低,然後就是性能,尤其是響應速度的提高。

 

最後說點務虛的東西:

說白了,編程這種工作,本質上就是一種工程學。既然是工程學,那就是要碰到實際問題時如何根據各種條件尋求優化解。這實際上是一種經驗積累而來的技能,說難不難說易不易:只有大量練習,熟能生巧這一條路——跟大家在高考前大量的題海戰術是一回事。

所以,以前在學校裏學了下,平時以各種藉口不用不思考不積累,等你真的要用想用的時候,用不用得起來,出不出洋相,還真的要看運氣了。那麼這些程序員,不管一開始的起點是高是低,反正一輩子的天花板,伸伸手就能夠得着了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章