Skip to content

Instantly share code, notes, and snippets.

@kirilloid
Last active January 7, 2018 04:25
Show Gist options
  • Save kirilloid/14ec1da29355b6dc0523743f600747e9 to your computer and use it in GitHub Desktop.
Save kirilloid/14ec1da29355b6dc0523743f600747e9 to your computer and use it in GitHub Desktop.

Revisions

  1. kirilloid revised this gist Jan 7, 2018. 2 changed files with 99 additions and 85 deletions.
    96 changes: 11 additions & 85 deletions deepClone.js
    Original file line number Diff line number Diff line change
    @@ -1,88 +1,14 @@
    // 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;
    function deepClone(node) {
    if (Array.isArray(node)) {
    return node.map(deepClone);
    }
    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);
    if (typeof node !== 'object'
    || node === null) {
    return node;
    }
    // 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));
    });
    var copy = {};
    for (var key in node) {
    copy[key] = deepClone(node[key]);
    }
    return copy;
    }
    88 changes: 88 additions & 0 deletions suite.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,88 @@
    // 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));
    });
    }
  2. kirilloid revised this gist Jan 7, 2018. 2 changed files with 91 additions and 62 deletions.
    24 changes: 24 additions & 0 deletions cloneViaPostMessage.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,24 @@
    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);
    });
    }
    129 changes: 67 additions & 62 deletions deepClone.js
    Original file line number Diff line number Diff line change
    @@ -1,83 +1,88 @@
    var ITEARATIONS_BASE = 1e6; // adjust it for slow browsers
    // the call hirerarhcy is: suite > run > runIteration

    // run it with testSuite(jsonClone)
    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;
    if (Math.random() > 0.1) {
    obj = {};
    for (let i = 0; i < num; i++) {
    obj[generateString()] = generate(depth - 1);
    }
    } else {
    obj = [];
    for (let i = 0; i < num; i++) {
    obj.push(generate(depth - 1));
    }
    }
    return obj;
    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 = generate(maxDepth);
    if (totalKeys < maxKeys) return;
    let start = performance.now();
    copyFn(obj);
    return performance.now() - start;
    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;
    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;
    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);
    }
    times = times
    .sort(function (a, b) { return a - b; })
    .slice(iterations * .1, -iterations * .1);
    return {
    avg: avg(times),
    disp: 3 * disp(times)
    };
    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(ITEARATIONS_BASE / nodes / 10) * 10);
    console.log(nodes, run(copyFn, iterations, depth, nodes));
    })
    [
    { 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));
    });
    }
  3. kirilloid created this gist Jan 6, 2018.
    83 changes: 83 additions & 0 deletions deepClone.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,83 @@
    var ITEARATIONS_BASE = 1e6; // adjust it for slow browsers

    // run it with testSuite(jsonClone)

    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;
    if (Math.random() > 0.1) {
    obj = {};
    for (let i = 0; i < num; i++) {
    obj[generateString()] = generate(depth - 1);
    }
    } else {
    obj = [];
    for (let i = 0; i < num; i++) {
    obj.push(generate(depth - 1));
    }
    }
    return obj;
    }
    var obj = generate(maxDepth);
    if (totalKeys < maxKeys) return;
    let start = performance.now();
    copyFn(obj);
    return performance.now() - start;
    }

    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);
    }
    times = times
    .sort(function (a, b) { return a - b; })
    .slice(iterations * .1, -iterations * .1);
    return {
    avg: avg(times),
    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(ITEARATIONS_BASE / nodes / 10) * 10);
    console.log(nodes, run(copyFn, iterations, depth, nodes));
    })
    }