前言
語雀地址:https://www.yuque.com/yangxiaofei-vquku/wmp1zm/mggrlr
基于堆疊式的架構的虛擬機所使用的零地址指令更加緊湊,單完成一項操作的時候必然需要使用更多的入堆疊和出堆疊指令,虛擬機堆疊也是存在于記憶體中,這就意味著將需要更多的指令分派次數和記憶體讀/寫次數,頻繁的執行記憶體讀/寫操作必然會影響執行速度,為了解決這一問題HotSpot JVM的設計者們提出了堆疊頂快取(Top-of-Stack-Caching)技術,將堆疊頂元素(或堆疊頂周邊)元素快取到物理CUP的暫存器中,以此降低對記憶體的讀寫次數,提升執行引擎的執行效率,
有關堆疊頂快取技術需要關注兩個核心問題:
- 快取了堆疊頂附近的多少個元素?如果快取了n個元素,那么就叫n-TOS caching;
- 快取帶有多少種“狀態”?如果有n種狀態那么就叫n-state TOS caching,
1. 先看n-TOS caching
從抽象資料結構來舉例運算元堆疊的實作:可以想像把Java標準庫自帶的那個java.util.Stack包裝一下,假如實作堆疊頂快取技術的邏輯如下:壓如運算元堆疊的堆疊頂元素優先存到CUP的暫存器中,(假設Stack為原始運算元堆疊,StackWith1TOSCA為支持堆疊頂快取的運算元堆疊)
import java.util.EmptyStackException;
import java.util.Stack;
public class StackWith1TOSCA<E> {
private enum TosState {
NOT_CACHED,
CACHED;
}
// 記憶體中的運算元堆疊(記憶體)
private Stack<E> theStack = new Stack<E>();
// 堆疊頂元素(暫存器)
private E topOfStackElement; // the cache
// 是否有快取,在1-TOS中即表示堆疊頂是否有資料
private TosState state = TosState.NOT_CACHED;
// 向運算元堆疊中壓如元素
public void push(E elem) {
// 如果堆疊頂已有元素,
if (state == TosState.CACHED) {
// 將堆疊頂元素壓入記憶體中真正的運算元堆疊
theStack.push(topOfStackElement);
}
// 將暫存器中堆疊頂資料替換為新壓人的堆疊頂元素
topOfStackElement = elem;
// 設定state為CACHE代表堆疊頂有元素
state = TosState.CACHED;
}
// 運算元堆疊的堆疊頂元素出堆疊
public E pop() {
// 如果狀態為NOT_CACHE也就是無堆疊頂元素,此時運算元堆疊為空無法出堆疊
if (state == TosState.NOT_CACHED) throw new EmptyStackException();
// 將CPU暫存器中的堆疊頂元素取出
E result = topOfStackElement;
// 記憶體中的堆疊楨運算元堆疊是否為空
if (theStack.isEmpty()) {
// 此時運算元堆疊為空,堆疊頂元素彈出為空
state = TosState.NOT_CACHED;
topOfStackElement = null;
} else {
// 將記憶體中運算元堆疊的堆疊頂元素去除放入暫存器
topOfStackElement = theStack.pop();
}
return result;
}
}
那么如果有這樣的Java代碼:
static void foo(Object o) {
Object temp = o;
}
對于位元組碼指令為

用不支持堆疊頂快取的運算元堆疊Stack執行上面位元組碼指令,執行了2次記憶體讀取兩次記憶體寫入,jvm操作為
// 從區域變數表讀取寫入運算元堆疊——》一次記憶體讀取,一次記憶體寫入
stack.push(locals[0]);
// 從運算元堆疊中讀取寫入區域變數表——》一次記憶體讀取,一次記憶體寫入
locals[1] = stack.pop();
用支持堆疊頂快取技術的運算元堆疊StackWith1TOSCA執行上面位元組碼指令,執行了一次記憶體讀取,一次暫存器寫入,一次暫存器讀取,一次記憶體寫入,jvm操作為
// 從區域變數表讀取,寫入暫存器——》一次記憶體讀取,一次暫存器寫入
topOfStackElement = locals[0];
// 從暫存器讀取,寫入區域變數表——》一次暫存器讀取,一次記憶體寫入
locals[1] = topOfStackElement;
從上述例子可以看出將堆疊頂元素快取暫存器有效的減少了記憶體的讀寫次數,而如果選擇把堆疊頂附近的若干個元素快取在暫存器里的話,在頻繁的計算邏輯中將大幅度提升性能,
2.再看n-state TOS caching
前面的StackWith1TOSCA例子里可以看到已經有“state”的概念出現:我們必須要知道當前在快取里到底有沒有值,不然就無從判斷壓堆疊/出堆疊時資料該何去何從了,這個例子用了兩種狀態,NOT_CACHED和CACHED;對于不關心堆疊里元素型別的stack caching來說,1-TOS用兩種狀態就夠用了,
實際上“狀態”可以記錄許多東西,取決于到底要實作怎樣的TOSCA,
- 一個例子:如果我們現在不用1-TOS,而用3-TOS caching的話,那很明顯我們的“狀態”不但要記錄“有沒有快取堆疊頂元素”,還得記錄“到底堆疊頂附近的三個元素到底放在哪個變數里了”,
- 另一個例子:如果我們的堆疊需要跟蹤堆疊里的元素的型別,同時我們使用1-TOS caching的話,那就意味著要記錄的“狀態”里必須記住堆疊頂元素是什么型別的,HotSpot VM的解釋器就是這樣的例子,它雖然只用了1-TOS caching,但它的TosState卻有9種有效值,也就是說這個解釋器的TOSCA可以描述為1-TOS, 9-state caching,
大家可以想像一個n > 1的n-TOS如果跟帶型別的TOSCA結合起來狀態數量的膨脹速度會有多快,
實際上多數虛擬機就算用了stack caching也只會用1-TOS,因為簡單高效;大不了1-TOS外帶型別,
也有復雜一些的例子,例如Sun JDK 1.1.x里的解釋器在x86上的實作,它用的是2-TOS, 3-state caching,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/291606.html
標籤:其他
上一篇:用C完成一個小游戲:三子棋
下一篇:CNN入門mnist資料集運行環境搭建(安裝Python,Pycharm,Anaconda,Tensorflow,CNN代碼)
