1. 程式人生 > >重學計算機組成原理(二)- 制定學習路線,攀登“效能”之巔

重學計算機組成原理(二)- 制定學習路線,攀登“效能”之巔

0 學習路線的知識點概括

學習計算機組成原理,就是學習計算機是如何協調執行的

  • 計算機組成原理的英文叫Computer Organization

Organization 意"組織機構"。

該組織機構能夠進行各種計算、控制、讀取輸入,進行輸出,達成各種強大的功能。

把整個計算機組成原理的知識點拆分成了四大部分

  • 計算機的基本組成
  • 計算機的指令和計算
  • 處理器設計
  • 儲存器和I/O裝置。

0.1 計算機的基本組成

計算機的硬體組成

這些硬體,怎麼對應到經典的馮·諾依曼體系結構的

除此之外,還需要了解計算機的兩個核心指標

  • 效能
  • 功耗

效能和功耗也是我們在應用和設計五大基本元件中需要重點考慮的因素。

0.2 計算機的指令和計算

需要搞明白,我們每天撰寫的一行行C、Java、PHP程式,是怎麼在計算機裡面跑起來的。

  • 瞭解我們的程式是怎麼通過編譯器和彙編器,變成一條條機器指令這樣的編譯過程(編譯過程展開,就是編譯原理)
  • 知道我們的作業系統是怎麼連結、裝載、執行這些程式的(深入學習,就是作業系統)。而這一條條指令執行的控制過程,就是由計算機五大元件之一的控制器來控制的。

計算部分,要從二進位制和編碼開始,理解我們的資料在計算機裡的表示,以及我們是怎麼從數位電路層面,實現加法、乘法這些基本的運算功能的。

實現這些運算功能的ALU(Arithmetic Logic Unit/ALU),算術邏輯單元,計算機五大元件之一的運算器。

特別重要的就是浮點數(Floating Point)。

浮點數是我們在日常運用中非常容易用錯的一種資料表示形式。掌握浮點數能讓你對資料的編碼、儲存和計算能夠有一個從表到裡的深入理解。尤其在AI火熱的今天,浮點數是機器學習中重度使用的資料表示形式,掌握它更是非常有必要。

0.3 CPU的設計

CPU時鐘可以用來構造暫存器和記憶體的鎖存器和觸發器,因此,CPU時鐘應該是我們學習CPU的前導知識。搞明白我們為什麼需要CPU時鐘(CPU Clock),以及暫存器和記憶體是用什麼樣的硬體組成的之後,我們可以再來看看,整個計算機的資料通路是如何構造出來的。

資料通路,其實就是連線了整個運算器和控制器,並最終組成了CPU。而出於對於效能和功耗的考慮,你要進一步理解和掌握面向流水線設計的CPU、資料和控制冒險,以及分支預測的相關技術。

既然CPU作為控制器要和輸入輸出裝置通訊,那麼我們就要知道異常和中斷髮生的機制。在CPU設計部分的最後,我會講一講指令的並行執行,看看如何直接在CPU層面,通過SIMD來支援平行計算。

0.4 儲存器的原理

通過儲存器的層次結構作為基礎的框架引導,需要掌握從上到下的CPU快取記憶體、記憶體、SSD硬碟和機械硬碟的工作原理,它們之間的效能差異,以及實際應用中利用這些裝置會遇到的挑戰。儲存器其實很多時候又扮演了輸入輸出裝置的角色,所以你需要進一步瞭解,CPU和這些儲存器之間是如何進行通訊的,以及我們最重視的效能問題是怎麼一回事;理解什麼是IO_WAIT,如何通過DMA來提升程式效能。

對於儲存器,我們不僅需要它們能夠正常工作,還要確保裡面的資料不能丟失。於是你要掌握我們是如何通過RAID、Erasure Code、ECC以及分散式HDFS,這些不同的技術,來確保資料的完整性和訪問效能。

計算機組成原理的學習辦法

相較於整個電腦科學中的其他科目,計算機組成原理更像是整個計算機學科裡的“綱要”。這門課裡任何一個知識點深入挖下去,都可以變成計算機科學裡的一門核心課程。

  • 程式怎樣從高階程式碼變成指令在計算機裡面執行,對應著“編譯原理”和“作業系統”這兩門課程
  • 計算實現背後則是“數位電路”
  • 如果要深入CPU和儲存器系統的優化,必然要深入瞭解“計算機體系結構”。

為了更快更好地學計算機組成,總結了三個學習方法

學會提問自己來串聯知識點

學完一個知識點之後,你可以從下面兩個方面,問一下自己。

  • 我寫的程式,是怎樣從輸入的程式碼,變成執行的程式,並得到最終結果的?
  • 整個過程中,計算器層面到底經歷了哪些步驟,有哪些地方是可以優化的?

無論是程式的編譯、連結、裝載和執行,以及計算時需要用到的邏輯電路、ALU,乃至CPU自發為你做的流水線、指令級並行和分支預測,還有對應訪問到的硬碟、記憶體,以及載入到快取記憶體中的資料,這些都對應著我們學習中的一個個知識點。建議你自己腦子裡過一遍,最好時口頭表述一遍或者寫下來,這樣對你徹底掌握這些知識點都會非常有幫助。

寫一些示例程式來驗證知識點

電腦科學是一門實踐的學科。計算機組成中的大量原理和設計,都對應著“效能”這個詞。因此,通過把對應的知識點,變成一個個效能對比的示例程式碼程式記錄下來,是把這些知識點融匯貫通的好方法。因為,相比於強記硬背知識點,一個有著明確性能對比的示例程式,會在你腦海裡留下更深刻的印象。當你想要回顧這些知識點的時候,一個程式也更容易提示你把它從腦海深處裡面找出來。

通過和計算機硬體發展的歷史做對照

計算機的發展並不是一蹴而就的。從第一臺電子計算機ENIAC(Electronic Numerical Integrator And Computer,電子數值積分計算機)的發明到現在,已經有70多年了。現代計算機用的各個技術,都是跟隨實際應用中遇到的挑戰,一個個發明、打磨,最後保留下來的。這當中不僅僅有學術層面的碰撞,更有大量商業層面的交鋒。通過了解充滿戲劇性和故事性的計算機硬體發展史,讓你更容易理解計算機組成中各種原理的由來。

比如說,奔騰4和SPARC的失敗,以及ARM的成功,能讓我們記住CPU指令集的繁與簡、權衡效能和功耗的重要性,而現今高速發展的機器學習和邊緣計算,又給計算機硬體設計帶來了新的挑戰。

學習資料推薦

最有效的辦法還是“讀書百遍,其義自見”。對於不夠明白的知識點,多搜尋,多看不同來源的資料,多和朋友、同事、老師一起交流,一定能夠幫你掌握好想要學習的知識點。

入門書籍

  • 《計算機是怎樣跑起來的》
  • 《程式是怎樣跑起來的》(入門微縮版“計算機組成原理”)
  • 計算機組成中,硬體層面的基礎實現,比如暫存器、ALU這些電路是怎麼回事Coursera上的北京大學免費公開課《Computer Organization》。

進階書籍

  • 《計算機組成與設計:硬體/軟體介面》
  • 《深入理解計算機系統(原書第3版》(CS APP的經典教材)

    該書的配套視訊
    • Bilibili版
    • Youtube版
  • 《計算機組成:結構化方法》
  • 《計算機體系結構:量化研究方法》

拓展閱讀

  • Redhat的What Every Programmer Should Know About Memory
  • LMAX開源的Disruptor實際應用程式
  • 《編碼:隱匿在計算機軟硬體背後的語言》

  • 《程式設計師的自我修養:連結、裝載和庫》
    理解計算機硬體和作業系統層面程式碼執行的優秀閱讀材料

路線學習小結

學習不是死記硬背,學習材料也不是越多越好。

最有效的辦法,不是短時間衝刺,而是有節奏地堅持,多在留言區和其他朋友一起交流,就更容易能夠“積小步而至千里”,在程式設計師這個職業上有更長足的發展。

歡迎重學計算機組成原理 ! ! !

  • 買電腦時,“原來的電腦效能跟不上啦”
  • 寫程式時,“這個程式效能可以優化一下”

這虛無縹緲的“效能”到底指的是什麼呢?

我們能不能給效能下一個明確的定義,然後來進行準確的比較呢?

在計算機組成原理乃至體系結構中,“效能”都是最重要的一個主題。

學習和研究計算機組成原理,就是在理解計算機是怎麼運作的,以及為什麼要這麼運作。

“為什麼”所要解決的事情,很多時候就是提升“效能”。

1 時間的倒數 - 效能

計算機的效能,其實和體力勞動很像,好比是我們要搬東西。

對於計算機的效能,我們需要有個標準來衡量。這個標準中主要有兩個指標。

1.1 響應時間(Response time)/ 執行時間(Execution time)

讓計算機“跑得更快”。

我們執行一個程式,到底需要花多少時間。花的時間越少,自然效能就越好。

  • 實際系統裡效能監測工具NewRelic中的響應時間,代表了每個外部的Web請求的執行時間

1.2 吞吐率(Throughput)/ 頻寬(Bandwidth)

讓計算機“搬得更多”。

伺服器使用的網路頻寬,通常就是一個吞吐率效能指標.吞吐率是指我們在一定的時間範圍內,到底能處理多少事情。這裡的“事情”,在計算機裡就是處理的資料或者執行的程式指令。

和搬東西對比,如果響應時間短,跑得快,我們可以來回多跑幾趟搬幾趟。

所以縮短程式的響應時間,一般來說都會提升吞吐率。

除了縮短響應時間,我們還有別的方法嗎?當然!

我們還可以多找幾個人一起來搬,這就類似伺服器都是多核的。

人多力量大,同時處理資料,在單位時間內就可以處理更多資料,吞吐率自然也就上去了。

提升吞吐率的辦法有很多。大部分時候,我們只要多加一些機器,多堆一些硬體就好了。

但是響應時間的提升卻沒有那麼容易,因為CPU的效能提升其實在10年前就處於“擠牙膏”的狀態了,所以我們得慎重地來分析對待。

下面我們具體來看。

我們一般把效能,定義成響應時間的倒數,也就是:

效能 = 1/響應時間

響應時間越短,效能數值越高。

同樣一個程式

  • 在Intel最新的CPU Coffee Lake上,只需要30s就能執行完成
  • 而在5年前CPU Sandy Bridge上,需要1min才能完成

那麼我們自然可以算出來,Coffee Lake的效能是1/30,Sandy Bridge的效能是1/60,兩個的效能比為2。於是,我們就可以說,Coffee Lake的效能是Sandy Bridge的2倍。

過去幾年流行的手機跑分軟體,就是把多個預設好的程式在手機上執行,然後根據執行需要的時間,算出一個分數來給出手機的效能評估。

而在業界,各大CPU和伺服器廠商組織了一個叫作SPEC(Standard Performance Evaluation Corporation)的第三方機構,專門用來指定各種“跑分”的規則

  • 一份SPEC報告通常包含了大量不同測試的評分

SPEC提供的CPU基準測試程式,就好像CPU屆的“高考”,通過數十個不同的計算程式,對於CPU的效能給出一個最終評分。

這些程式豐富多彩,有編譯器、直譯器、視訊壓縮、人工智慧國際象棋等等,涵蓋了方方面面的應用場景。感興趣的話,你可以點選這裡

2 計算機的計時單位:CPU時鐘

雖然時間是一個很自然的用來衡量效能的指標,但是用時間來衡量時,有兩個問題。

2.1 時間的測不準原理

如果用你自己隨便寫的一個程式,來統計程式執行的時間,每一次統計結果不會完全一樣。

為什麼會不準呢?這裡面有好幾個原因。

2.1.1 統計時間方法

我們統計時間是用類似於“掐秒錶”一樣,記錄 程式執行結束時間減去程式開始執行的時間。

這個時間也叫Wall Clock Time或者Elapsed Time

就是在執行程式期間,掛在牆上的鐘走掉的時間。

但計算機可能同時執行著好多個程式,CPU實際上不停地在各個程式之間進行切換。

在這些走掉的時間裡,很可能CPU切換去執行別的程式了。

而且,有些程式在執行的時候,可能要從網路、硬碟去讀取資料,要等網路和硬碟把資料讀出來,給到記憶體和CPU。

要想準確統計某程式的實際執行時間,進而比較程式之間的效能,須把這些額外時間除掉

那這件事怎麼實現呢???

Linux下有一個叫time的命令,可助我們一臂之力,同樣的Wall Clock Time下,程式實際在CPU上到底花了多少時間。

我們簡單執行一下time命令。它會返回三個值

  • 第一個 real time
    也就是我們說的Wall Clock Time,即執行程式整個過程中流逝掉的時間
  • 第二個 user time
    CPU在執行你的程式,在使用者態執行指令的時間
  • 第三個sys time
    CPU執行你的程式,在作業系統核心裡執行指令的時間

程式實際花費的CPU執行時間(CPU Time),就是user time加上sys time

一般情況下,如果user+sys比real大,甚至僅user比real大的情況出現,都是因為對應的程式被多個程序或者多個執行緒並行執行了,也很常見。

在多核或者多cpu的機器上執行,seq和wc命令會分配到兩個cpu上,user和sys是兩個cpu時間相加的,而real只是現實時鐘裡走過的時間,極端情況下user+sys可以到達real的兩倍

雖然seq和wc這兩個命令都是單執行緒執行的,但是這兩個命令在多核cpu執行的情況下,會分別分配到兩個不同的cpu,於是user和sys的時間都是兩個cpu上執行的時間之和,就可能超過real的時間。

可以這樣來快速驗證,執行

time seq 100000000 | wc -l &

讓這個命令多跑一會兒,並且在後臺執行。

然後利用 top 命令看不同程序的cpu佔用情況

你會在top的前幾行裡看到seq和wc的cpu佔用都接近100,實際是各被分配到了一個不同的cpu執行。

2.1.2 不一定可直接比較出效能

即使我們已經拿到了CPU時間,我們也不一定可以直接“比較”出兩個程式的效能差異

即使在同一臺計算機上,CPU可能滿載執行也可能降頻執行,降頻執行的時候自然花的時間會多一些。

除CPU外,時間這個效能指標還會受到主機板、記憶體這些其他相關硬體的影響。

所以,我們需要對“時間”這個我們可以感知的指標進行

2.2 CPU時間拆解

程式的CPU執行時間=CPU時鐘週期數×時鐘週期時間
  • 時鐘週期時間
    你在買電腦的時候,一定關注過CPU的主頻
    我手頭的這臺電腦就是
  • 2.8 GHz Intel Core i7

這裡的2.8GHz就是電腦的主頻(Frequency/Clock Rate)。

這個2.8GHz,我們可以先簡單地理解為,CPU在1秒時間內,可以執行的簡單指令的數量是2.8G條。

更準確點,這個2.8GHz就代表,我們CPU的一個“鐘錶”能夠識別出來的最小的時間間隔。

就像我們掛在牆上的掛鐘,都是一秒一秒地走,所以通過牆上的掛鐘能夠識別出來的最小時間單位就是秒。

而在CPU內部,有一個叫晶體振盪器(Oscillator Crystal)的東西,簡稱為晶振。

把晶振當成CPU內部的電子錶來使用。

晶振帶來的每一次“滴答”,就是時鐘週期時間。

在我這個2.8GHz的CPU上,這個時鐘週期時間,就是1/2.8G。

我們的CPU,是按照這個“時鐘”提示的時間來進行自己的操作。

主頻越高,意味著這個表走得越快,我們的CPU也就走得越快。

“超頻”,這說的其實就相當於把買回來的CPU內部的鐘給調快了,於是CPU的計算跟著這個時鐘的節奏,也就自然變快了。當然這個快不是沒有代價的,CPU跑得越快,散熱的壓力也就越大。就和人一樣,超過生理極限,CPU就會崩潰了。

  • 回顧之前的CPU執行時間的公式程式的CPU執行時間=CPU時鐘週期數×時鐘週期時間

最簡單的提升效能方案,自然縮短時鐘週期時間,也就是提升主頻。

換句話說,就是換一塊好一點的CPU。

不過,這個是我們這些底層的研發人員控制不了的,所以我們就把目光挪到了乘法的另一個因子——CPU時鐘週期數上。

如果能夠減少程式需要的CPU時鐘週期數量,一樣能夠提升程式效能。

  • 對於CPU時鐘週期數,我們可以再做一個分解,把它變成CPU時鐘週期數 = 指令數×每條指令的平均時鐘週期數(Cycles Per Instruction,簡稱CPI)不同的指令需要的Cycles是不同的
    加法和乘法都對應著一條CPU指令
    但是乘法需要的Cycles就比加法要多,自然也就慢。
    在這樣拆分了之後,我們的程式的CPU執行時間就可以變成這樣三個部分的乘積。
程式的CPU執行時間=指令數 × CPI × Clock Cycle Time

因此,要解決效能問題,就是要優化這三者。

  • 時鐘週期時間
    就是計算機主頻,取決於硬體。摩爾定律就一直在不停提高的主頻。
    最早的80386主頻只有33MHz,現在手頭的膝上型電腦就有2.8GHz,在主頻層面,就提升了將近100倍。
  • 每條指令的平均時鐘週期數CPI
    一條指令到底需要多少CPU Cycle。在後面講解CPU結構的時候,我們會看到,現代的CPU通過流水線技術(Pipeline),讓一條指令需要的CPU Cycle儘可能地少。因此,對於CPI的優化,也是計算機組成和體系結構中的重要一環。
  • 指令數
    代表執行我們的程式到底需要多少條指令、用哪些指令。
    這個很多時候就把挑戰交給了編譯器。
    同樣的程式碼,編譯成計算機指令時候,就有各種不同的表示方式。

把自己想象成一個CPU,坐在那裡寫程式。
計算機主頻就好像是你的打字速度,打字越快,你自然可以多寫一點程式。

CPI相當於你在寫程式的時候,熟悉各種快捷鍵,越是打同樣的內容,需要敲擊鍵盤的次數就越少。

指令數相當於你的程式設計得夠合理,同樣的程式要寫的程式碼行數就少。

如果三者皆能實現,你自然可以很快地寫出一個優秀的程式,“效能”從外面來看就是好的。

3 總結

學完本文,對“效能”這個名詞,你應該有了更清晰的認識。

主要對於“響應時間”這個效能指標進行抽絲剝繭,拆解成了計算機時鐘週期、CPI以及指令數這三個獨立的指標的乘積,並且為指明瞭優化計算機效能的三條康莊大道。

也就是,提升計算機主頻,優化CPU設計使得在單個時鐘週期內能夠執行更多指令,以及通過編譯器來減少需要的指令數。

後面會講解,具體怎麼在電路硬體、CPU設計,乃至指令設計層面,提升計算機的效能。

參考

  • 豆瓣讀書 - 計算機專題
  • 深入淺出計算機組成原理
  • What do 'real', 'user' and 'sys' mean in the output of time(1)?