jsPerf.app is an online JavaScript performance benchmark test runner & jsperf.com mirror. It is a complete rewrite in homage to the once excellent jsperf.com now with hopefully a more modern & maintainable codebase.
jsperf.com URLs are mirrored at the same path, e.g:
https://jsperf.com/negative-modulo/2
Can be accessed at:
https://jsperf.app/negative-modulo/2
function deepFreeze(obj) {
const stack = [obj];
const visited = new WeakSet();
while (stack.length > 0) {
const current = stack.pop();
if (visited.has(current)) continue;
visited.add(current);
Object.freeze(current);
for (const key of Reflect.ownKeys(current)) {
const value = current[key];
if (
value !== null &&
(typeof value === "object" || typeof value === "function") &&
!Object.isFrozen(value)
) {
stack.push(value);
}
}
}
return obj;
}
function deepFreeze2(object) {
// Retrieve the property names defined on object
const propNames = Reflect.ownKeys(object);
// Freeze properties before freezing self
for (const name of propNames) {
const value = object[name];
if ((value && typeof value === "object") || typeof value === "function") {
deepFreeze(value);
}
}
return Object.freeze(object);
}
function cowFreeze(obj) {
return new Proxy(obj, {
set(target, prop, value) {
throw new Error("Immutable: cannot modify frozen object");
},
get(target, prop) {
const val = target[prop];
if (val && typeof val === "object") {
return cowFreeze(val); // lazily wrap nested objects
}
return val;
}
});
}
// Large fake object manufacturer
function createMockData({ depth = 4, breadth = 6, includeCycles = true, crossLinkRatio = 0.02 } = {}) {
// nodes list to optionally create cross-links
const nodes = [];
function makeNode(level, path) {
const node = { __id: path.join('.') };
nodes.push(node);
// add many primitive props to increase size
for (let i = 0; i < Math.min(8, breadth); i++) {
node['p' + i] = `val-${path.join('-')}-${i}`;
}
// add symbol keys occasionally
if (level % 2 === 0) {
const s = Symbol('sym' + path.join('-'));
node[s] = `symval-${path.join('-')}`;
}
if (level < depth) {
node.children = [];
for (let b = 0; b < breadth; b++) {
const child = makeNode(level + 1, path.concat(b));
node.children.push(child);
}
} else {
// leaf: add a large array to increase memory footprint
node.leafArray = new Array(Math.min(200, breadth * 20)).fill(0).map((_, i) => `${path.join('-')}-item-${i}`);
}
return node;
}
const root = makeNode(0, ['root']);
if (includeCycles) {
// add some self-loops and cross-links
for (let i = 0; i < Math.max(1, Math.floor(nodes.length * 0.005)); i++) {
const idx = Math.floor(Math.random() * nodes.length);
nodes[idx].self = nodes[idx]; // self-loop
}
// cross links based on ratio
const crossCount = Math.floor(nodes.length * crossLinkRatio);
for (let i = 0; i < crossCount; i++) {
const a = nodes[Math.floor(Math.random() * nodes.length)];
const b = nodes[Math.floor(Math.random() * nodes.length)];
if (a && b && a !== b) {
// attach a cross reference
(a.crossRefs || (a.crossRefs = [])).push(b);
}
}
// optional parent references using WeakMap style external map
// (not attached to objects to avoid extra cycles unless desired)
}
return { root, _meta: { createdAt: Date.now(), nodeCount: nodes.length } };
}
const small = createMockData({ depth: 1, breadth: 2, includeCycles: false });
const big = createMockData(({ depth: 2, breadth: 3, includeCycles: true, crossLinkRatio: 0.005 }));
Ready to run.
| Test | Ops/sec | |
|---|---|---|
| (Small Object) Stack + WeakSet Cycle Detection | | ready |
| (Small Object) Recursively Freeze | | ready |
| (Small Object) Copy-on-write | | ready |
| (Big Object) Stack + WeakSet Cycle Detection | | ready |
| (Big Object) Recursively Freeze | | ready |
| (Big Object) Proxy copy-on-write | | ready |
You can edit these tests or add more tests to this page by appending /edit to the URL.