JavaScript implementation Twoqueues Caching model

Source: Internet
Author: User
Tags object constructor end insert key prev return version

This article refers to the Twoqueues caching model, which is to say that data is cached in memory.

In any language, you might want to put some data in memory, avoiding duplication and reading. The most common scenario is the jquery selector, where the selection of DOM elements is time-consuming, and we want to cache the data so that we don't have to iterate over the DOM tree every time we call it.

Save it, but there's gotta be a measure! Always can not put all the historical data in memory, after all, the current memory capacity is still very poor, even if the memory is large enough, theoretically each thread allocated memory is limited.

So the question is, how can we effectively cache the really useful data? This involves the elimination algorithm, the need to eliminate the garbage data, in order to keep useful data.

More commonly used ideas have the following several:



FIFO: Is an advanced first out of the queue, the first cached data, the first to be eliminated, the well-known jquery framework is used inside the model.

LRU: Double linked list structure, every time there is a new data deposit, directly placed in the chain table head, each accessed data, also transferred to the chain header, so that the end of the list of data is not recently used, eliminated.

twoqueues:fifo+ Lru,fifo mainly store the initial deposit of the data, LRU stored at least two hot spot data, this algorithm has high hit rate, adaptability, low complexity.



There are many other elimination algorithms, but the actual use of more of these two kinds. Because their own algorithm is not complex, easy to implement, efficient execution, cache hit rate in most cases also acceptable. After all, caching algorithms also need to consume CPU, if too complex, although the hit rate has improved, but not outweigh the gains. Imagine, if the data from the cache, than from the original location of the consumption of time, to cache what?

Concrete theory is not much to say, there are some online, I do not understand, today to share is the JavaScript version of the twoqueues cache model.

Let's talk about the way to use it first.

The basic use method is as follows:

var TQ = inittwoqueues (10);
Tq.set ("Key", "value");
Tq.get ("key");

When initializing, specify the cache capacity. It should be noted that, because the internal adoption of FIFO+LRU implementation, so the actual capacity is twice times the specified capacity, the above example specified 10 (key-value pairs), can actually store 20.

Capacity size needs to be based on actual application scenarios, too small, too low, too inefficient, extremes, need to measure their own.

During the development process, you can initialize the cache pool to a development version in order to review the caching effect:

var TQ = Inittwoqueues (a true);
Tq.hitratio ();

is to add a parameter to the back, direct true on it. This initializes the cache pool, automatically statistics the hit rate, and can get the hit rate through the Hitratio method. If this argument is not added, the Hitratio method gets a hit rate that is always 0.

Statistical hit rate is definitely to consume resources, so the production environment is not recommended to open.

It's time to share the code:

(function (exports) {

    / **
     * Pure class for inheritance
     * @constructor
     * /
    function Fn () {}
    Fn.prototype = Elimination.prototype;

    / **
     * Parent class of cache elimination algorithm based on linked list
     * @param maxLength cache capacity
     * @constructor
     * /
    function Elimination (maxLength) {
        this.container = ();
        this.length = 0;
        this.maxLength = maxLength 30;
        this.linkHead = this.buildNode ("", "");
        this.linkHead.head = true;
        this.linkTail = this.buildNode ("", "");
        this.linkTail.tail = true;

        this.linkHead.next = this.linkTail;
        this.linkTail.prev = this.linkHead;
    }

    Elimination.prototype.get = function (key) {
        throw new Error ("This method must be override!");
    };

    Elimination.prototype.set = function (key, value) {
        throw new Error ("This method must be override!");
    };

    / **
     * Create nodes in a linked list
     * @param data The data contained in the node, ie the cached data value
     * @param key The unique identifier of the node, which is the cached key
     * @returns {(}}
     * /
    Elimination.prototype.buildNode = function (data, key) {
        var node = ();
        node.data = data;
        node.key = key;
        node.use = 0;

        return node;
    };

    / **
     * Pop a node from the head of the linked list
     * @returns {*}
     * /
    Elimination.prototype.shift = function () {
        var node = null;
        if (! this.linkHead.next.tail) {
            node = this.linkHead.next;
            this.linkHead.next = node.next;
            node.next.prev = this.linkHead;

            delete this.container [node.key];
            this.length--;
        }

        return node;
    };

    / **
     * Insert a node from the head of the linked list
     * @param node node object
     * @returns {*}
     * /
    Elimination.prototype.unshift = function (node) {
        node.next = this.linkHead.next;
        this.linkHead.next.prev = node;

        this.linkHead.next = node;
        node.prev = this.linkHead;

        this.container [node.key] = node;
        this.length ++;

        return node;
    };

    / **
     * Insert a node from the end of the linked list
     * @param node node object
     * @returns {*}
     * /
    Elimination.prototype.append = function (node) {

        this.linkTail.prev.next = node;
        node.prev = this.linkTail.prev;

        node.next = this.linkTail;
        this.linkTail.prev = node;

        this.container [node.key] = node;
        this.length ++;

        return node;
    };

    / **
     * Pop a node from the end of the linked list
     * @returns {*}
     * /
    Elimination.prototype.pop = function () {
        var node = null;

        if (! this.linkTail.prev.head) {
            node = this.linkTail.prev;
            node.prev.next = this.linkTail;
            this.linkTail.prev = node.prev;

            delete this.container [node.key];
            this.length--;
        }

        return node;
    };

    / **
     * Remove the specified node from the linked list
     * @param node node object
     * @returns {*}
     * /
    Elimination.prototype.remove = function (node) {
        node.prev.next = node.next;
        node.next.prev = node.prev;

        delete this.container [node.key];
        this.length--;

        return node;
    };

    / **
     * The processing required for the node to be accessed, specifically moving the node to the head of the list
     * @param node
     * /
    Elimination.prototype.use = function (node) {
        this.remove (node);
        this.unshift (node);
    };


    / **
     * LRU cache elimination algorithm implementation
     * @constructor
     * /
    function LRU () {
        Elimination.apply (this, arguments);
    }
    LRU.prototype = new Fn ();

    LRU.prototype.get = function (key) {
        var node = undefined;

        node = this.container [key];

        if (node) {
            this.use (node);
        }

        return node;
    };

    LRU.prototype.set = function (key, value) {
        var node = this.buildNode (value, key);

        if (this.length === this.maxLength) {
            this.pop ();
        }

        this.unshift (node);
    };


    / **
     * FIFO buffer elimination algorithm
     * @constructor
     * /
    function FIFO () {
        Elimination.apply (this, arguments);
    }
    FIFO.prototype = new Fn ();

    FIFO.prototype.get = function (key) {
        var node = undefined;

        node = this.container [key];

        return node;
    };

    FIFO.prototype.set = function (key, value) {
        var node = this.buildNode (value, key);

        if (this.length === this.maxLength) {
            this.shift ();
        }

        this.append (node);
    };


    / **
     * LRU, FIFO algorithm package, becoming the new twoqueues cache elimination algorithm
     * @param maxLength
     * @constructor
     * /
    function Agent (maxLength) {
        this.getCount = 0;
        this.hitCount = 0;
        this.lir = new FIFO (maxLength);
        this.hir = new LRU (maxLength);
    }

    Agent.prototype.get = function (key) {
        var node = undefined;

        node = this.lir.get (key);

        if (node) {
            node.use ++;
            if (node.use> = 2) {
                this.lir.remove (node);
                this.hir.set (node.key, node.data);
            }
        } else {
            node = this.hir.get (key);
        }

        return node;
    };

    Agent.prototype.getx = function (key) {
        var node = undefined;

        this.getCount ++;
node = this.get (key);

        if (node) {
            this.hitCount ++;
        }

        return node;
    };

    Agent.prototype.set = function (key, value) {
        var node = null;

        node = this.lir.container [key] this.hir.container [key];

        if (node) {
            node.data = value;
        } else {
            this.lir.set (key, value);
        }
    };

    / **
     * Get hit rate
     * @returns {*}
     * /
    Agent.prototype.hitRatio = function () {
        var ret = this.getCount;

        if (ret) {
            ret = this.hitCount / this.getCount;
        }

        return ret;
    };

    / **
     * External Interface
     * @param maxLength cache capacity
     * @param dev is the development environment, the development environment will count the hit rate, otherwise it will not
     * @returns {(get, set: Function, hitRatio: Function)}
     * /
    exports.initTwoQueues = function (maxLength, dev) {

        var api = new Agent (maxLength);

        return {
            get: (function () {
                if (dev) {
                    return function (key) {
                        var ret = api.getx (key);
                        return ret && ret.data;
                    };
                } else {
                    return function (key) {
                        var ret = api.get (key);
                        return ret && ret.data;
                    };
                }
            ) ()),
            set: function () {
                api.set.apply (api, arguments);
            },
            hitRatio: function () {
                return api.hitRatio.apply (api, arguments);
            }
        };

    };


} (this));  


Finally, again, the caching algorithm needs to be combined with the actual application scene, there is no universal algorithm, the right is the best!




Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.