Vue的緩存算法—LRU算法

最近在看Vue的源碼,不得不說的是,Vue的源碼十分優雅簡潔,下面就來分享下Vue的緩存利用的算法LRU算法。html

LRU算法

LRU是Least recently used的簡寫,主要原理是根據歷史訪問記錄來淘汰數據,說白了就是這個算法認爲若是數據被訪問過,那麼未來被訪問的概率也高。其存儲結構是一個雙鏈表,最近被訪問到的放在雙鏈表的尾部,頭部放的就是最先被訪問到數據。關於算法的具體流程,能夠來看下這個,這個可視化過程,模擬了lru算法進行調度的過程。vue

缺頁數

lru在筆試題中也會常常出現,常常會考到的那就是缺頁數,例如頁面訪問序列爲:2,3,2,1,5,2,4,5,3,2,5,2, 分配給某個進程3頁內存,求其缺頁次數。
缺頁數能夠理解爲,內存不滿的次數,轉到lru來看就是鏈表中有空節點的次數。下面來走一下整個流程(左爲head右爲tail):node

  1. 2            // 第一次缺頁git

  2. 2 -> 3    // 第二次缺頁github

  3. 3 -> 2    // 第三次缺頁算法

  4. 3 -> 2 -> 1緩存

  5. 2 -> 1   // 第四次缺頁函數

  6. 2 -> 1 -> 5this

  7. 1 -> 5 -> 2prototype

  8. 5 -> 2   // 第五次缺頁

  9. 5 -> 2 -> 4

  10. 2 -> 4 -> 5

  11. 4 -> 5   // 第六次缺頁

  12. 4 -> 5 -> 3

  13. 5 -> 3    // 第七次缺頁

  14. 5 -> 3 -> 2

  15. 3 -> 2 -> 5

  16. 3 -> 5 -> 2

因此總共有着7次缺頁,上面的這個流程也是算法的具體執行流程,能夠看出的是當有新的節點進入時,首先會檢測內存是否已滿,若是滿了的話,就先將頭給移除,再在尾部添加上這個新節點;倘若該節點在鏈表中存在,那麼直接將這個節點拿到頭部。下面來看下Vue對這個算法的實現:

vue中的lru

源碼時src/cache.js,先來看看其構造函數:

// limit是最大容量
function Cache (limit) {
    this.size = 0;
    this.limit = limit;
    this.head = this.tail = undefined;
    this._keymap = Object.create(null);
}

尤大利用集合_keymap來存儲已有的節點,在判斷是否存在時,直接讀取屬性就行,不用在遍歷一遍鏈表,這樣下降了在查找過程當中的時間複雜度。head表明着頭節點,tail表明着尾節點,鏈表中的節點是這樣的:

node {
    value: 鍵值,
    key: 鍵名,
    older: 指向前一個節點,head的older指向undefined,
    newer: 指向下一個節點,tail的newer指向undefined
}

來看get方法:

Cache.prototype.get = function (key, returnEntry) {
     var entry = this._keymap[key];
     // 自己沒有,則不用調度,直接將新的節點插入到尾部便可
    if (entry === undefined) return;
    // 訪問的就是尾部節點,則不須要調度    
    if (entry === this.tail) {
        return returnEntry ? entry : entry.value;
    }
    // 訪問的不是尾部節點,須要將被訪問節點拿到頭部
    if (entry.newer) {
        if (entry === this.head) {
            this.head = entry.newer;
        }
        entry.newer.older = entry.older;
    }
    if (entry.older) {
        entry.older.newer = entry.newer;
    }
    entry.newer = undefined;
    entry.older = this.tail;
    if (this.tail) {
        this.tail.newer = entry;
    }
    this.tail = entry;
    return returnEntry ? entry : entry.value;
 };

get是爲了獲得鏈表中是否含有這個節點,假若有這個節點,那麼還要對這個節點進行調度,也就是將節點拿到尾部。

// 將鏈表的頭給去除
Cache.prototype.shift = function () {
    var entry = this.head;
    if (entry) {
        this.head = this.head.newer;
        this.head.older = undefined;
        entry.newer = entry.older = undefined;
        this._keymap[entry.key] = undefined;
        this.size--;
    }
    return entry;
};
p.put = function (key, value) {
    var removed;
    var entry = this.get(key, true);
    // 插入的狀況,插入到尾部
    if (!entry) {
        // 若是集合已經滿了,移除一個,並將其return
        if (this.size === this.limit) {
            removed = this.shift();
        }
        entry = {
            key: key
        };
        this._keymap[key] = entry;
        if (this.tail) {
            this.tail.newer = entry;
            entry.older = this.tail;
        } else {  // 鏈表中插入第一個節點時,頭節點就是尾幾點
            this.head = entry;
        }
        this.tail = entry;   // 節點會添加或者拿到尾部
        this.size++;
    }
    // 更新節點的value,倘若自己鏈表中有,get方法中已經調度好,只要更新值就好
    entry.value = value;
    return removed;
};

至此,vue的cache代碼已經所有解析完,其具體的做用因爲源碼剛剛開始讀嗎,目前還不清楚,不過應該在解析指令等方面有着重大的做用。

最後但願你們關注下算法演示

相關文章
相關標籤/搜索