深入理解計算機系統(tǒng)重點筆記

引言

深入理解計算機系統(tǒng)首懈,對我來說是部大塊頭勋功。說實話,我沒有從頭到尾完完整整的全部看完,而是選擇性的看了一些我自認為重要的或感興趣的章節(jié)欲芹,也從中獲益良多枯冈,看清楚了計算機系統(tǒng)的一些本質(zhì)東西或原理性的內(nèi)容华坦,這對每個想要深入學(xué)習(xí)編程的程序員來說都是至關(guān)重要的撮躁。只有很好的理解了系統(tǒng)到底是如何運行我們代碼的,我們才能針對系統(tǒng)的特點寫出高質(zhì)量捺氢、高效率的代碼來藻丢。這本書我以后還需要多研究幾遍,今天就先總結(jié)下書中我已學(xué)到的幾點知識摄乒。


重點筆記

  1. 編寫高效的程序需要下面幾類活動:
  • 選擇一組合適的算法和數(shù)據(jù)結(jié)構(gòu)郁岩。這是很重要的,好的數(shù)據(jù)結(jié)構(gòu)有時能幫助更快的實現(xiàn)某些算法缺狠,這也要求編程人員能夠熟知各種常用的數(shù)據(jù)結(jié)構(gòu)和算法。
  • 編寫出使編譯器能夠有效優(yōu)化以轉(zhuǎn)換成高效可執(zhí)行的源代碼萍摊。因此挤茄,理解編譯器優(yōu)化的能力和局限性是很重要的。編寫程序方式中看上去只是一點小小的變動冰木,都會引起編譯器優(yōu)化方式很大的變化穷劈。有些編程語言比其他語言容易優(yōu)化得多笼恰。C語言的某些特性,例如執(zhí)行指針運算和強制類型轉(zhuǎn)換的能力歇终,使得編譯器很難對其進行優(yōu)化社证。
  • 并行技術(shù),針對處理運算量特別大的計算评凝,將一個任務(wù)分成多個部分追葡,這些部分可以在多核和多處理器的某種組合上并行地計算。
  1. 讓編譯器展開循環(huán)
    說到程序優(yōu)化奕短,很多人都會提到循環(huán)展開技術(shù)∫巳猓現(xiàn)在編譯器可以很容易地執(zhí)行循環(huán)展開,只要優(yōu)化級別設(shè)置的足夠高翎碑,許多編譯器都能例行公事的做到這一點谬返。用命令行選項“-funroll-loops”調(diào)用gcc,會執(zhí)行循環(huán)展開日杈。

  2. 性能提高技術(shù):

  • 高級設(shè)計遣铝,為手邊的問題選擇適當(dāng)?shù)乃惴ê蛿?shù)據(jù)結(jié)構(gòu),要特別警覺莉擒,避免使用會漸進地產(chǎn)生糟糕性能的算法或編碼技術(shù)酿炸。
  • 基本編碼原則。避免限制優(yōu)化的因素啰劲,這樣編譯器就能產(chǎn)生高效代碼梁沧。
    • 消除連續(xù)的函數(shù)調(diào)用。在可能時將計算移到循環(huán)外蝇裤,考慮有選擇的妥協(xié)程序的模塊性以獲得更大效率廷支。
    • 消除不必要的存儲器引用。引入臨時變量來保存中間結(jié)果栓辜,只有在最后的值計算出來時恋拍,才能將結(jié)果放到數(shù)組或全局變量中。
  • 低級優(yōu)化藕甩。
    • 嘗試各種與數(shù)組代碼相對的指針形式施敢。
    • 通過開展通過展開循環(huán)降低循環(huán)開銷。
    • 通過諸如迭代分割之類的技術(shù)狭莱,找到使用流水線化的功能單元的方法僵娃。

說到性能提高,可能有人會有一些說法:

(1)不要過早優(yōu)化腋妙,優(yōu)化是萬惡之源默怨;
(2)花費很多時間所作的優(yōu)化可能效果不明顯,不值得骤素;
(3)現(xiàn)在內(nèi)存匙睹、CPU價格都這么低了愚屁,性能的優(yōu)化已經(jīng)不是那么重要了。
 ……

其實我的看法是:我們也許不必特地把以前寫過的程序拿出來優(yōu)化下痕檬,花費N多時間只為提升那么幾秒或幾分鐘的時間霎槐。但是,我們在重構(gòu)別人的代碼或自己最初開始構(gòu)思代碼時梦谜,就需要知道這些性能提高技術(shù)丘跌,一開始就遵守這些基本原則來寫代碼,寫出的代碼也就不需要讓別人來重構(gòu)以提高性能了改淑。另外碍岔,有的很簡單的技術(shù),比如說將與循環(huán)無關(guān)的復(fù)雜計算或大內(nèi)存操作的代碼放到循環(huán)外朵夏,對于整個性能的提高真的是較明顯的蔼啦。

  1. 如何使用代碼剖析程序(code profiler,即性能分析工具)來調(diào)優(yōu)代碼仰猖?
    程序剖析(profiling)其實就是在運行程序的一個版本中插入了工具代碼捏肢,以確定程序的各個部分需要多少時間。
    Unix系統(tǒng)提供了一個profiling叫GPROF饥侵,這個程序產(chǎn)生兩類信息:

首先鸵赫,它確定程序中每個函數(shù)花費了多少CPU時間。
其次躏升,它計算每個函數(shù)被調(diào)用的次數(shù)辩棒,以執(zhí)行調(diào)用的函數(shù)來分類。還有每個函數(shù)被哪些函數(shù)調(diào)用膨疏,自身又調(diào)用了哪些函數(shù)一睁。

使用GPROF進行剖析需要3個步驟,比如源程序為prog.c佃却。
1)編譯: gcc -O1 -pg prog.c -o prog(只要加上-pg參數(shù)即可)
2)運行:./prog
 會生成一個gmon.out文件供 gprof分析程序時候使用(運行比平時慢些)者吁。
3)剖析:gprof prog
 分析gmon.out中的數(shù)據(jù),并顯示出來饲帅。
剖析報告的第一部分列出了執(zhí)行各個函數(shù)花費的時間复凳,按照降序排列。
剖析報告的第二部分是函數(shù)的調(diào)用歷史灶泵。
具體例子可參考網(wǎng)上資料育八。

GPROF有些屬性值得注意:

  • 計時不是很準確。它的計時基于一個簡單的間隔計數(shù)機制赦邻,編譯過的程序為每個函數(shù)維護一個計數(shù)器髓棋,記錄花費在執(zhí)行該函數(shù)上的時間。對于運行時間較長的程序深纲,相對準確仲锄。
  • 調(diào)用信息相當(dāng)可靠。
  • 默認情況下湃鹊,不顯示庫函數(shù)的調(diào)用儒喊。相反地,庫函數(shù)的時間會被計算到調(diào)用它們的函數(shù)的時間中币呵。
  1. 靜態(tài)鏈接和動態(tài)鏈接一個很重要的區(qū)別是:動態(tài)鏈接時沒有任何動態(tài)鏈接庫的代碼和數(shù)據(jù)節(jié)真正的被拷貝到可執(zhí)行文件中怀愧,反之,鏈接器只需拷貝一些重定位和符號表信息余赢,即可使得運行時可以解析對動態(tài)鏈接庫中代碼和數(shù)據(jù)的引用芯义。

  2. 存儲器映射
    指的是將磁盤上的空間映射為虛擬存儲器區(qū)域。Unix進程可以使用mmap函數(shù)來創(chuàng)建新的虛擬存儲器區(qū)域妻柒,并將對象映射到這些區(qū)域中扛拨,這屬于低級的分配方式。
    一般C程序會使用malloc和free來動態(tài)分配存儲器區(qū)域举塔,這是利用堆的方式绑警。

  3. 造成堆利用率很低的主要原因是碎片,當(dāng)雖然有未使用的存儲器但不能用來滿足分配請求時央渣,就會發(fā)生這種現(xiàn)象计盒。
    有兩種形式的碎片:內(nèi)部碎片和外部碎片。兩者的區(qū)別如下:

  • 內(nèi)部碎片是在一個已分配的塊比有效載荷大時發(fā)生的芽丹。例如北启,有些分配器為了滿足對其約束添加額外的1字的存儲空間,這個1字的空間就是內(nèi)部碎片拔第。它就是已分配塊大小和它們的有效載荷大小之差的和咕村。
  • 外部碎片是當(dāng)空閑存儲器合計起來足夠滿足一個分配請求,但是沒有一個單獨的空閑塊足夠大可以來處理這個請求時發(fā)生的楼肪。
  1. 現(xiàn)代OS提供了三種方法實現(xiàn)并發(fā)編程:
  • 進程培廓。用這種方法,每個邏輯控制流都是一個進程春叫,由內(nèi)核來調(diào)度和維護肩钠。因為進程有獨立的虛擬地址空間,想要和其他流通信暂殖,控制流必須使用進程間通信(IPC)价匠。
  • I/O多路復(fù)用。這種形式的并發(fā)呛每,應(yīng)用程序在一個進程的上下文中顯示地調(diào)度它們自己的邏輯流踩窖。邏輯流被模擬為“狀態(tài)機”,數(shù)據(jù)到達文件描述符后晨横,主程序顯示地從一個狀態(tài)轉(zhuǎn)換到另一個狀態(tài)洋腮。因為程序是一個單獨的進程箫柳,所以所有的流都共享一個地址空間。
  • 線程啥供。線程是運行在一個單一進程上下文中的邏輯流悯恍,由內(nèi)核進行調(diào)度。線程可以看做是進程和I/O多路復(fù)用的合體伙狐,像進程一樣由內(nèi)核調(diào)度涮毫,像I/O多路復(fù)用一樣共享一個虛擬地址空間。

(1)基于進程的并發(fā)服務(wù)器
構(gòu)造并發(fā)最簡單的就是使用進程贷屎,像fork函數(shù)罢防。例如,一個并發(fā)服務(wù)器唉侄,在父進程中接受客戶端連接請求咒吐,然后創(chuàng)建一個新的子進程來為每個新客戶端提供服務(wù)。為了了解這是如何工作的美旧,假設(shè)我們有兩個客戶端和一個服務(wù)器渤滞,服務(wù)器正在監(jiān)聽一個監(jiān)聽描述符(比如描述符3)上的連接請求。下面顯示了服務(wù)器是如何接受這兩個客戶端的請求的榴嗅。

進程并發(fā)示例

關(guān)于進程的優(yōu)劣妄呕,對于在父、子進程間共享狀態(tài)信息嗽测,進程有一個非常清晰的模型:共享文件表绪励,但是不共享用戶地址空間。進程有獨立的地址控件愛你既是優(yōu)點又是缺點唠粥。由于獨立的地址空間疏魏,所以進程不會覆蓋另一個進程的虛擬存儲器。但是另一方面進程間通信就比較麻煩晤愧,至少開銷很高大莫。

(2)基于I/O多路復(fù)用的并發(fā)編程
比如一個服務(wù)器,它有兩個I/O事件:1)網(wǎng)絡(luò)客戶端發(fā)起連接請求官份,2)用戶在鍵盤上鍵入命令行只厘。我們先等待那個事件呢?沒有那個選擇是理想的舅巷。如果accept中等待連接羔味,那么無法響應(yīng)輸入命令。如果在read中等待一個輸入命令钠右,我們就不能響應(yīng)任何連接請求(這個前提是一個進程)赋元。
針對這種困境的一個解決辦法就是I/O多路復(fù)用技術(shù)。基本思想是:使用select函數(shù),要求內(nèi)核掛起進程搁凸,只有在一個或者多個I/O事件發(fā)生后媚值,才將控制返給應(yīng)用程序。
I/O多路復(fù)用的優(yōu)劣:由于I/O多路復(fù)用是在單一進程的上下文中的护糖,因此每個邏輯流程都能訪問該進程的全部地址空間杂腰,所以開銷比多進程低得多;缺點是編程復(fù)雜度高椅文。

(3)基于線程的并發(fā)編程
每個線程都有自己的線程上下文,包括一個線程ID惜颇、棧皆刺、棧指針、程序計數(shù)器凌摄、通用目的寄存器和條件碼羡蛾。所有的運行在一個進程里的線程共享該進程的整個虛擬地址空間。由于線程運行在單一進程中锨亏,因此共享這個進程虛擬地址空間的整個內(nèi)容痴怨,包括它的代碼、數(shù)據(jù)器予、堆浪藻、共享庫和打開的文件。所以我認為不存在線程間通信乾翔,線程間只有鎖的概念爱葵。

  • 線程執(zhí)行的模型。線程和進程的執(zhí)行模型有些相似反浓。每個進程的生明周期都是一個線程萌丈,我們稱之為主線程。但是大家要有意識:線程是對等的雷则,主線程跟其他線程的區(qū)別就是它先執(zhí)行辆雾。
    一般來說,線程的代碼和本地數(shù)據(jù)被封裝在一個線程例程中(就是一個函數(shù))月劈。該函數(shù)通常只有一個指針參數(shù)和一個指針返回值度迂。
    在Unix中線程可以是joinable(可結(jié)合)或者detached(分離)的。joinable可以被其他線程殺死艺栈,detached線程不能被殺死英岭,它的存儲器資源有系統(tǒng)自動釋放。

    • 線程存儲器模型湿右,每個線程都有它自己的獨立的線程上下文诅妹,包括線程ID、棧、棧指針吭狡、程序計數(shù)器尖殃、條件碼和通用目的寄存器。每個線程和其他線程共享剩下的部分划煮,包括整個用戶虛擬地址空間送丰,它是由代碼段、數(shù)據(jù)段弛秋、堆以及所有的共享庫代碼和數(shù)據(jù)區(qū)域組成器躏。不同線程的棧是對其他線程不設(shè)防的,也就是說:如果一個線程以某種方式得到一個指向其他線程的指針蟹略,那么它可以讀取這個線程棧的任何部分登失。
  1. 什么樣的變量多線程可以共享,什么樣的不可以共享挖炬?
    有三種變量:全局變量揽浙、本地自動變量(局部變量)和本地靜態(tài)變量,其中本地自動變量每個線程的本地棧中都存有一份意敛,不共享馅巷。而全局變量和靜態(tài)變量可以共享。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末草姻,一起剝皮案震驚了整個濱河市钓猬,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌撩独,老刑警劉巖逗噩,帶你破解...
    沈念sama閱讀 212,454評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異跌榔,居然都是意外死亡异雁,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評論 3 385
  • 文/潘曉璐 我一進店門僧须,熙熙樓的掌柜王于貴愁眉苦臉地迎上來纲刀,“玉大人,你說我怎么就攤上這事担平∈景恚” “怎么了?”我有些...
    開封第一講書人閱讀 157,921評論 0 348
  • 文/不壞的土叔 我叫張陵暂论,是天一觀的道長面褐。 經(jīng)常有香客問我,道長取胎,這世上最難降的妖魔是什么展哭? 我笑而不...
    開封第一講書人閱讀 56,648評論 1 284
  • 正文 為了忘掉前任湃窍,我火速辦了婚禮城看,結(jié)果婚禮上芭梯,老公的妹妹穿的比我還像新娘。我一直安慰自己琴儿,他們只是感情好役衡,可當(dāng)我...
    茶點故事閱讀 65,770評論 6 386
  • 文/花漫 我一把揭開白布茵休。 她就那樣靜靜地躺著,像睡著了一般手蝎。 火紅的嫁衣襯著肌膚如雪榕莺。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,950評論 1 291
  • 那天棵介,我揣著相機與錄音帽撑,去河邊找鬼。 笑死鞍时,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的扣蜻。 我是一名探鬼主播逆巍,決...
    沈念sama閱讀 39,090評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼莽使!你這毒婦竟也來了锐极?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,817評論 0 268
  • 序言:老撾萬榮一對情侶失蹤芳肌,失蹤者是張志新(化名)和其女友劉穎灵再,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體亿笤,經(jīng)...
    沈念sama閱讀 44,275評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡翎迁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,592評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了净薛。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片汪榔。...
    茶點故事閱讀 38,724評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖肃拜,靈堂內(nèi)的尸體忽然破棺而出痴腌,到底是詐尸還是另有隱情,我是刑警寧澤燃领,帶...
    沈念sama閱讀 34,409評論 4 333
  • 正文 年R本政府宣布士聪,位于F島的核電站,受9級特大地震影響猛蔽,放射性物質(zhì)發(fā)生泄漏剥悟。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,052評論 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望懦胞。 院中可真熱鬧替久,春花似錦、人聲如沸躏尉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽胀糜。三九已至颅拦,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間教藻,已是汗流浹背距帅。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留括堤,地道東北人碌秸。 一個月前我還...
    沈念sama閱讀 46,503評論 2 361
  • 正文 我出身青樓,卻偏偏與公主長得像悄窃,于是被迫代替她去往敵國和親讥电。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,627評論 2 350

推薦閱讀更多精彩內(nèi)容