Skip to content

Commit

Permalink
Add run-benchmark script
Browse files Browse the repository at this point in the history
  • Loading branch information
spautz committed Aug 27, 2020
1 parent 3caa6be commit 5a5a8f3
Show file tree
Hide file tree
Showing 8 changed files with 189 additions and 1 deletion.
9 changes: 9 additions & 0 deletions benchmark/libraries/immer.js
@@ -0,0 +1,9 @@
const immer = require('immer');

const immerCase = {
label: 'immer',
setWithString: null,
setWithArray: null,
};

module.exports = immerCase;
9 changes: 9 additions & 0 deletions benchmark/libraries/immutable-assign.js
@@ -0,0 +1,9 @@
const immutableAssign = require('immutable-assign');

const immutableAssignCase = {
label: 'immutable-assign',
setWithString: null,
setWithArray: null,
};

module.exports = immutableAssignCase;
9 changes: 9 additions & 0 deletions benchmark/libraries/immutable.js
@@ -0,0 +1,9 @@
const immutable = require('immutable');

const immutableCase = {
label: 'immutable.js',
setWithString: null,
setWithArray: null,
};

module.exports = immutableCase;
12 changes: 12 additions & 0 deletions benchmark/libraries/lodash.js
@@ -0,0 +1,12 @@
const setWith = require('lodash/setWith');
const clone = require('lodash/clone');

const lodashImmutableSet = (obj, path, value) => setWith(clone(obj), path, value, clone);

const lodashCase = {
label: 'lodash',
setWithString: lodashImmutableSet,
setWithArray: lodashImmutableSet,
};

module.exports = lodashCase;
9 changes: 9 additions & 0 deletions benchmark/libraries/seamless-immutable.js
@@ -0,0 +1,9 @@
const seamlessImmutable = require('seamless-immutable');

const seamlessImmutableCase = {
label: 'seamless-immutable',
setWithString: null,
setWithArray: null,
};

module.exports = seamlessImmutableCase;
9 changes: 9 additions & 0 deletions benchmark/libraries/tiny-immutable-set.js
@@ -0,0 +1,9 @@
const { set } = require('tiny-immutable-set');

const tinyImmutableSetCase = {
label: 'tiny-immutable-set',
setWithString: set,
setWithArray: set,
};

module.exports = tinyImmutableSetCase;
2 changes: 1 addition & 1 deletion benchmark/package.json
Expand Up @@ -17,7 +17,7 @@
},
"sideEffects": false,
"scripts": {
"benchmark": "node ./benchmark.js"
"benchmark": "node ./run-benchmark.js"
},
"dependencies": {
"@nelsongomes/ts-timeframe": "^0.2.2",
Expand Down
131 changes: 131 additions & 0 deletions benchmark/run-benchmark.js
@@ -0,0 +1,131 @@
const { Timeline } = require('@nelsongomes/ts-timeframe');

const allLibraries = [
require('./libraries/immer'),
require('./libraries/immutable'),
require('./libraries/immutable-assign'),
require('./libraries/lodash'),
require('./libraries/seamless-immutable'),
require('./libraries/tiny-immutable-set'),
];

const allScenarios = [runStringScenario, runArrayScenario, runStringScenario, runArrayScenario];

const iterationsPerRun = 25000;

// An object of arrays of objects, with 5 items at each level
const TEST_OBJECT_BREADTH = 10;
const TEST_OBJECT = {};
for (let propNum = 0; propNum < TEST_OBJECT_BREADTH; propNum++) {
TEST_OBJECT[`prop${propNum}`] = [];
for (let indexNum = 0; indexNum < TEST_OBJECT_BREADTH; indexNum++) {
const obj = {};
for (let deepNum = 0; deepNum < TEST_OBJECT_BREADTH; deepNum++) {
obj[`deep${deepNum}`] = deepNum;
}
TEST_OBJECT[`prop${propNum}`].push(obj);
}
}

//////////////////////////////////
function deepFreeze(object) {
// Retrieve the property names defined on object
var propNames = Object.getOwnPropertyNames(object);

// Freeze properties before freezing self

for (let name of propNames) {
let value = object[name];

if (value && typeof value === 'object') {
deepFreeze(value);
}
}

return Object.freeze(object);
}
deepFreeze(TEST_OBJECT);
//////////////////////////////////

function runStringScenario(libraryInfo) {
const { label, setWithString, timeline } = libraryInfo;

if (setWithString) {
// Pre-generate all paths
const allPaths = [];
for (let i = 0; i < iterationsPerRun; i++) {
const propNum = i % TEST_OBJECT_BREADTH;
const indexNum = (i / TEST_OBJECT_BREADTH) % TEST_OBJECT_BREADTH;
const deepNum = (i / (TEST_OBJECT_BREADTH * TEST_OBJECT_BREADTH)) % TEST_OBJECT_BREADTH;
allPaths.push(`${propNum}[${indexNum}].${deepNum}`);
}

const event = timeline.startEvent(['string-path']);
for (let i = 0; i < iterationsPerRun; i++) {
setWithString(TEST_OBJECT, allPaths[i], i);
}
event.end();
} else {
console.warn(`Library "${label}" is missing the setWithArray scenario`);
}
}

function runArrayScenario(libraryInfo) {
const { label, setWithArray, timeline } = libraryInfo;

if (setWithArray) {
// Pre-generate all paths
const allPaths = [];
for (let i = 0; i < iterationsPerRun; i++) {
const prop = i % TEST_OBJECT_BREADTH;
const index = (i / TEST_OBJECT_BREADTH) % TEST_OBJECT_BREADTH;
const deep = (i / (TEST_OBJECT_BREADTH * TEST_OBJECT_BREADTH)) % TEST_OBJECT_BREADTH;
allPaths.push([prop, index, deep]);
}

const event = timeline.startEvent(['array-path']);
for (let i = 0; i < iterationsPerRun; i++) {
setWithArray(TEST_OBJECT, allPaths[i], i);
}
event.end();
} else {
console.warn(`Library "${label}" is missing the setWithArray scenario`);
}
}

// We're going to run through each (library, scenario) tuple twice:
// 1. For each library, run each scenario type
// 2. For each scenario type, run each library

for (let libNum = 0; libNum < allLibraries.length; libNum++) {
allLibraries[libNum].timeline = new Timeline();

for (let scenarioNum = 0; scenarioNum < allScenarios.length; scenarioNum++) {
allScenarios[scenarioNum](allLibraries[libNum]);
}
}

for (let scenarioNum = 0; scenarioNum < allScenarios.length; scenarioNum++) {
for (let libNum = 0; libNum < allLibraries.length; libNum++) {
allScenarios[scenarioNum](allLibraries[libNum]);
}
}

// Now let's take a look at the results

for (let libNum = 0; libNum < allLibraries.length; libNum++) {
allLibraries[libNum].timeline.end();

const { label, timeline } = allLibraries[libNum];
const [seconds, nanoseconds] = timeline.sumEventsDuration();
const totalCount = timeline.count();
const averageTime = (seconds * 1e9) / totalCount + nanoseconds / totalCount;
console.log(`==== ${label} ====`);
console.log(
`Average time: ${seconds}.${nanoseconds}/${totalCount} = ${averageTime}ns (${averageTime /
1e9}s)`,
);

console.log(timeline.getDuration());
console.log(timeline.generateAnalyticInfo());
}

0 comments on commit 5a5a8f3

Please sign in to comment.