本文是轉載,原文地址:技術奇異點
什么是寄存器
寄存器 (register) 似乎是初學計算機結構的人多少有所了解的概念 —— 成本最高也是速度最快的存儲單元。不過,有時事物還可以從完全不同的角度重新闡述一遍。這段時間在看 SICP 和 Lua 虛擬機,其中兩個常被并列提及的概念是 stack-based 虛擬機和 reigster 虛擬機。即使在深入了解虛擬機理論前,也可以想像語言虛擬機結構不太可能與 CPU 硬件 register 有直接對應關系。「Register 虛擬機」這個名稱引起我對 register 進行重新認識的興趣。
接觸虛擬機之前還考慮過另一個和 register 相關的問題。一般的常識是函數調用時的參數通過 stack 傳遞。但是很多編譯器從 90 年代起就實現了一種優(yōu)化:通過 CPU register 傳遞參數。這樣做的優(yōu)點固然顯而易見,但同時引出新的疑問:原本用 stack 傳遞參數不僅解決了數據傳遞問題,也解決了多層嵌套的函數調用時上層數據的保存問題;而 register 數量有限,如何解決多層嵌套調用時的狀態(tài)問題?答案其實也簡單,當一個函數 A 調用另一個函數 B 時,那些正在存儲 A 本身的參數或者 A 本身的局部變量的 register 如果又要被用于向 B 傳遞參數,那么這些 register 的內容在被更改為傳遞給 B 的參數之前會先被壓入 stack,B 返回之后這些 register 的內容會從 stack 中恢復。也就是說,register 并不是替代 stack 的傳參功能,而是對 stack 的使用延遲了一個調用層次。根據「局部性原理」延遲使用主存中的 stack 得到速度優(yōu)化。
由此可見,register[1]雖然經常被稱為比主存更快的存儲單元,但是它的作用并不是給任意用途的主存單元提供高速替代品,而是和 stack 這種特定用途的存儲區(qū)域有更緊密的聯系。若感覺如此得出結論過于倉促,可以再觀察幾個例子。比如說,操作系統進行線程切換時,需要切換當前 stack 以及 —— 所有 register 內容???,register 就像 stack 的延展。我們可以說 register 是 stack 最頂一段的硬件加速機制。
接下來再看 Lua 虛擬機的實現,它的 VM register 就是實現在 VM stack[2]上。也就是說,Lua 使用 VM stack 的方式其實和一般的 C 編譯器產生的代碼使用 CPU stack 的方式沒有區(qū)別。那么究竟為何 Lua 之類的虛擬機區(qū)別于 stack-based 虛擬機而特地被稱為 register 虛擬機呢?通過 stack-base 虛擬機的指令可以發(fā)現,這種虛擬機是基本嚴格按照后入先出的方式使用 stack,每條指令基本上只操作 stack top 的一兩條數據。而 register 虛擬機只在大的調用層次上遵循后入先出,在每層函數運行中是完全隨機地使用本層函數的 stack (即當前 stack-frame [3],函數返回地址部分除外)。
全新的 register 的本質定義是:可被隨機訪問的最頂層 stack-frame。由此可以看出,主流編譯語言幾乎都可以稱為 register 虛擬機 [4] 實現,即使在使用 register 傳參這個優(yōu)化流行之前。CPU 的硬件 register 是對一般 register 的硬件優(yōu)化。
腳注:
[1] :更確切地說是通用寄存器,general-purpose registers 。
[2]:Lua 的 VM stack 和其虛擬機實現本身的 C runtime stack (或者說硬件 CPU 的 stack) 是分開的。
[3]:按照函數調用層次,stack 可以被看作一組 frame,每層函數調用占據一個 frame。
[4]:這里的「虛擬機」是真實的 CPU。