Skip to content

Instantly share code, notes, and snippets.

@louisremi
Created May 17, 2016 14:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save louisremi/8987ec055d00a33d72f45d8fd9e82daf to your computer and use it in GitHub Desktop.
Save louisremi/8987ec055d00a33d72f45d8fd9e82daf to your computer and use it in GitHub Desktop.
Caching strategies (https://jsbench.github.io/#8987ec055d00a33d72f45d8fd9e82daf) #jsbench #jsperf
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>Caching strategies</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/benchmark/1.0.0/benchmark.min.js"></script>
<script src="./suite.js"></script>
</head>
<body>
<h1>Open the console to view the results</h1>
<h2><code>cmd + alt + j</code> or <code>ctrl + alt + j</code></h2>
</body>
</html>
"use strict";
(function (factory) {
if (typeof Benchmark !== "undefined") {
factory(Benchmark);
} else {
factory(require("benchmark"));
}
})(function (Benchmark) {
var suite = new Benchmark.Suite;
Benchmark.prototype.setup = function () {
function Cache1(limit) {
this.limit = limit || 1000;
this.map = new Map();
}
Cache1.prototype.set = function(key, value) {
this.map.delete(key);
this.map.set(key, value);
if ( this.map.size > this.limit ) {
this.map.delete( this.map.keys().next().value );
}
return this;
};
Cache1.prototype.get = function(key) {
if ( this.map.has(key) ) {
var value = this.map.get(key);
this.map.delete(key);
this.map.set(key, value);
return value;
}
};
Cache1.prototype.clear = function() {
this.map.clear();
};
function Cache2(limit) {
this.size = 0;
this.limit = limit || 1000;
this.map = {};
this.head = null;
this.tail = null;
}
Cache2.prototype._setHead = function(node) {
node.next = this.head;
node.prev = null;
if (this.head !== null) {
this.head.prev = node;
}
this.head = node;
if (this.tail === null) {
this.tail = node;
}
this.size++;
this.map[node.key] = node;
}
Cache2.prototype.set = function(key, value) {
if (key in this.map) {
this.map[key].value = value;
this.remove(key);
} else {
if (this.size >= this.limit) {
delete this.map[this.tail.key];
this.size--;
this.tail = this.tail.prev;
this.tail.next = null;
}
}
this._setHead({ key: key, value: value });
return this;
};
Cache2.prototype.get = function(key) {
if (key in this.map) {
var value = this.map[key].value;
var node = { key: key, value: value };
this.remove(key);
this._setHead(node);
return value;
}
};
Cache2.prototype.remove = function(key) {
if ( !(key in this.map) ) {
return false;
}
var node = this.map[key];
if (node.prev != null) {
node.prev.next = node.next;
} else {
this.head = node.next;
}
if (node.next != null) {
node.next.prev = node.prev;
} else {
this.tail = node.prev;
}
delete this.map[key];
this.size--;
return true;
};
Cache2.prototype.clear = function() {
this.map = {};
this.head = null;
this.tail = null;
};
function Cache3(limit) {
this.map0 = new Map();
this.map1 = new Map();
this.limit = limit || 1000;
}
Cache3.prototype.set = function(key, value) {
this.map0.set(key, value);
// before the cache reaches it's max size, start filling a new cache
if ( this.map0.size > this.limit * 0.9 ) {
this.map1.set( key, value );
}
// when the cache reaches its maximum size, swap it with the newest and
// clear it
if ( this.map0.size > this.limit ) {
var tmp = this.map0;
this.map0 = this.map1;
this.map1 = tmp;
this.map1.clear();
}
return this;
};
Cache3.prototype.get = function(key) {
return this.map0.get(key);
};
Cache3.prototype.delete = function(key) {
this.map1.delete(key);
return this.map0.delete(key);
};
Cache3.prototype.clear = function() {
this.map0.clear();
this.map1.clear();
}
var caches = [
new Map(),
new Cache1(1000),
new Cache2(1000),
new Cache3(1000)
];
caches.forEach(function(cache) {
for ( var i = 0; i < 1000; i++ ) {
cache.set(i, i * i);
}
});
};
suite.add("var key = Math.round(Math.random() * 1000);", function () {
var key = Math.round(Math.random() * 1000);
if ( Math.random() > 0 ) {
caches[0].get(key);
}
});
suite.add("var key = Math.round(Math.random() * 1000);", function () {
var key = Math.round(Math.random() * 1000);
if ( Math.random() > 0 ) {
caches[1].get(key);
}
});
suite.add("var key = Math.round(Math.random() * 1000);", function () {
var key = Math.round(Math.random() * 1000);
if ( Math.random() > 0 ) {
caches[2].get(key);
}
});
suite.add("var key = Math.round(Math.random() * 1000);", function () {
var key = Math.round(Math.random() * 1000);
if ( Math.random() > 0 ) {
caches[3].get(key);
}
});
suite.add("var key = Math.round(Math.random() * 1000);", function () {
var key = Math.round(Math.random() * 1000);
if ( Math.random() > 0 ) {
caches[0].set(key, key * key);
}
});
suite.add("var key = Math.round(Math.random() * 1000);", function () {
var key = Math.round(Math.random() * 1000);
if ( Math.random() > 0 ) {
caches[1].set(key, key * key);
}
});
suite.add("var key = Math.round(Math.random() * 1000);", function () {
var key = Math.round(Math.random() * 1000);
if ( Math.random() > 0 ) {
caches[2].set(key, key * key);
}
});
suite.add("var key = Math.round(Math.random() * 1000);", function () {
var key = Math.round(Math.random() * 1000);
if ( Math.random() > 0 ) {
caches[3].set(key, key * key);
}
});
suite.on("cycle", function (evt) {
console.log(" - " + evt.target);
});
suite.on("complete", function (evt) {
console.log(new Array(30).join("-"));
var results = evt.currentTarget.sort(function (a, b) {
return b.hz - a.hz;
});
results.forEach(function (item) {
console.log((idx + 1) + ". " + item);
});
});
console.log("Caching strategies");
console.log(new Array(30).join("-"));
suite.run();
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment