寄存器虛擬機

前面說到,虛擬機是真機的一種模擬,而棧虛擬機模擬的是基於棧計算的機器,和現在常見的基於寄存器的硬件機器不同,於是相應的也有基於寄存器的虛擬機,不過這個虛擬機可能跟真機差別比較大 

首先可以看看真機用寄存器的原因,計算機的存儲有一個規律,訪問速度越快的存儲,單價(單位容量的成本)越高,因此實際實現的時候,容量會很受限,反之因爲便宜而容量可以很大的存儲,儲存速度就慢,訪問速度(或單價)從高到底大致是寄存器,cpu cache,內存,機械磁盤,磁帶等,當然也有像ssd這種,介於內存和機械磁盤之間,爲了減少耦合和簡化設計,cpu的計算是面向寄存器(或類似的計算芯片,例如x86的FPU是基於棧實現,當然這個棧不是內存上的,是硬件內置),對於內存,主要是load和store操作,也就是說,做運算時將數據從內存load到寄存器,計算過後再store到內存裏,編譯器會分析出某些變量如果store後會被再次load,就不會load,因爲已經在寄存器了;或者發現某些變量在一段代碼中反覆store到內存再load出來使用,可能直接把這些操作都優化了,只保留寄存器操作,可以大幅度提高執行速度。當然,寄存器越多,這種優化的空間就越大 

寄存器虛擬機的指令,和彙編指令差距不大,就不詳細說明了,和棧虛擬機的指令的差別在於,運算大都是二地址或三地址指令 

如果和基於棧的模型比較(比如上面說的FPU),可以看到大致就是把棧換成寄存器堆,假設不考慮寄存器數量的問題,以及寄存器存取速度,則區別似乎在於: 
1 棧是單向增長的,只能對棧頂做push和pop操作,如果pop中間的內容,則需要O(N)時間的移動(實際上也可以通過標記刪除來避免移動,但增加了複雜性) 
2 寄存器的數據store後,可以繼續使用 
3 棧只能運算棧頂的若干數據,如果運算的數據可能重複,需要拷貝,比如a=a+a: 
load a 
copy_top 
add 
store a 
而寄存器架構的就可以: 
load r0, a 
add r0, r0, r0 
store a, r0 
看上去似乎是寄存器架構優勢,但是如果我們考慮具體情況,差距並沒有那麼大,對於1,大多數運算在理論上是基於AST的,就算用了寄存器,也是按棧的順序計算罷了,而對於2,我們只要給棧增加一個store_no_pop指令即可,兩者在數據存取和運算上差別不是很大 

如果考慮到虛擬機,那麼連存取速度優勢都不存在了,因爲是用內存來模擬寄存器,其實還是內存拷來拷去。不過,由於寄存器虛擬機的架構是模擬真機,因此在具體的虛擬機實現時,可以將虛擬寄存器映射到真實寄存器(運行時修改字節碼爲機器碼之類的)以提高運行速度,但反過來說,棧虛擬機架構的棧也是能映射的,我們只需要將棧頂n個元素映射到n個寄存器,剩下無法映射的留在內存裏,如果有入棧,則將邏輯上最下面的寄存器壓入內存棧,然後寄存器輪轉 

那麼,如果不考慮硬件映射,純粹討論虛擬機實現,兩者究竟誰有優勢呢,一般認爲從執行速度來看,寄存器虛擬機還是有些優勢的,原因在於,寄存器虛擬機可以充分利用寄存器做臨時結果存儲和結果複用,減少load和store的次數,另外,棧虛擬機的指令雖然更緊湊(參數少,一條指令甚至可以不超過兩個字節),佔用內存少,但是相應的會執行更多次的解釋循環,而基於寄存器的虛擬機能用更少的指令數量來完成同樣的工作(儘管每條指令都比較大),具體的,可以參考這篇VEE的論文,解釋比較清楚,VEE是ACM旗下一個專門研究虛擬執行環境的會議: 
http://www.usenix.org/events/vee05/full_papers/p153-yunhe.pdf 
這篇論文顯示,基於寄存器架構的虛擬機執行速度還是能比棧架構的快不少,即便用上threaded code技術(後面再講) 

不過話說回來,寄存器架構省略load和store在棧架構上也是能部分實現的,可以增加一些複雜指令,比如a=a+a: 
load_add_result a a 
store a 
反正讀進來也是在內存做運算,不如直接算好load進來,對於複雜指令a=a+b-c*d/e: 
load_add_result a b 
load_mul_result c d 
load_div_result STACK_TOP e 
store a 
只是這樣做的似乎不多 

既然寄存器虛擬機執行快些,而且硬件映射也簡單,那爲何java,python,ruby等主流實現都是基於棧的虛擬機呢,個人覺得有幾點原因: 
一,棧虛擬機字節碼的編譯器實現簡單,不需要考慮太多的臨時空間分配問題 
二,不同機器的寄存器數量甚至硬件架構都不同,用棧架構可以做到最大移植性,比如,代碼要運行在一個基於棧的硬件架構上,則棧虛擬機可以直接映射,就算在寄存器硬件架構上也可以映射,但反過來寄存器架構的虛擬代碼映射到棧架構硬件上可能不是那麼容易,這是一個包容性的問題,就好像前面說的java轉C++容易,C++轉java就不太容易 
三,目標機器的指令cache可能比較小,棧架構的代碼緊湊比較適合 
四,真要爲了效率的話,與其爲了30%的效率提升而將編譯過程複雜化,倒不如把精力投在jit上,收益更大
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章