Instantly share code, notes, and snippets.

Embed
What would you like to do?
deep clone test suite
var handlers = {};
self.addEventListener('message', function (event) {
if (event.origin !== location.origin) return;
var key = event.data.key;
var value = event.data.value;
if (!handlers[key]) return;
handlers[key](value);
delete handlers[key];
});
var TIMEOUT_ERROR = new Error('timed out');
function cloneViaPostMessage(arg) {
return new Promise(function (resolve, reject) {
var randomKey = Math.random().toString(36).slice(2);
handlers[randomKey] = resolve;
try {
self.postMessage({ key: randomKey, value: arg }, location.origin);
} catch(e) {
reject(e);
}
setTimeout(reject, 1000, TIMEOUT_ERROR);
});
}
function deepClone(node) {
if (Array.isArray(node)) {
return node.map(deepClone);
}
if (typeof node !== 'object'
|| node === null) {
return node;
}
var copy = {};
for (var key in node) {
copy[key] = deepClone(node[key]);
}
return copy;
}
// the call hirerarhcy is: suite > run > runIteration
var ITERATIONS_BASE = 1e6; // reduce it for slow browsers/methods
function jsonClone(x) { return JSON.parse(JSON.stringify(x)); }
function message(x) { window.postMessage(x, location.origin); }
function generateString () { return Math.random().toString(36).slice(2); }
function runIteration(maxDepth, maxKeys, copyFn) {
var totalKeys = 0;
function generate(depth) {
const num = depth / 2 + Math.floor(Math.random() * depth);
if (num === 0 || totalKeys >= maxKeys) {
totalKeys++;
return generateString();
}
var obj;
// actually the proportion of arrays/objects almost doesn't affect executino time for most functions
if (Math.random() > 0.1) {
obj = {};
for (var i = 0; i < num; i++) {
obj[generateString()] = generate(depth - 1);
}
} else {
obj = [];
for (var i = 0; i < num; i++) {
obj.push(generate(depth - 1));
}
}
return obj;
}
var obj = generate(maxDepth);
// it generates slightly more nodes, but no more than maxKeys + depth, which is negligible
if (totalKeys < maxKeys) return;
var start = performance.now();
copyFn(obj);
return performance.now() - start;
}
// stats utility functions
function avg(array) {
return array.reduce(function(a, b) { return a + b; }, 0) / array.length;
}
function disp(array) {
var avg2 = Math.pow(avg(array), 2);
var sq2 = array.reduce(function (a, e) { return a + e * e - avg2; }, 0);
return Math.sqrt(sq2) / array.length;
}
function run(copyFn, iterations, maxDepth, maxKeys) {
var times = [];
while (times.length < iterations * 1.2) {
var time = runIteration(maxDepth, maxKeys, copyFn);
if (time) times.push(time);
}
// remove top and bottom 1/12
times = times
.sort(function (a, b) { return a - b; })
.slice(iterations * .1, -iterations * .1);
return {
avg: avg(times),
// actual results seem suspiciously small, but gives some estimations anyway
disp: 3 * disp(times)
};
}
function suite(copyFn) {
[
{ depth: 10, nodes: 1000 },
{ depth: 10, nodes: 2000 },
{ depth: 11, nodes: 3000 },
{ depth: 11, nodes: 5000 },
{ depth: 11, nodes: 7000 },
{ depth: 12, nodes: 10000 },
{ depth: 12, nodes: 14000 },
{ depth: 12, nodes: 20000 },
{ depth: 13, nodes: 30000 },
{ depth: 13, nodes: 50000 },
{ depth: 13, nodes: 70000 },
{ depth: 13, nodes: 100000 }
].forEach(function (params) {
var depth = params.depth;
var nodes = params.nodes;
var iterations = Math.max(20, Math.round(ITERATIONS_BASE / nodes / 10) * 10);
console.log(nodes, run(copyFn, iterations, depth, nodes));
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment