benchmark: rework assert benchmarks for correctness

This reworks most assert benchmarks to provide more reliable test
cases that also test more cases than before while keeping the
runtime low.

Signed-off-by: Ruben Bridgewater <ruben@bridgewater.de>
PR-URL: https://github.com/nodejs/node/pull/46593
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
This commit is contained in:
Ruben Bridgewater
2023-02-09 22:07:11 +01:00
committed by Antoine du Hamel
parent 1d5058d4bb
commit 841279d79c
7 changed files with 123 additions and 131 deletions

View File

@@ -6,27 +6,47 @@ const bench = common.createBenchmark(main, {
n: [2e4],
len: [1e2, 1e3],
strict: [0, 1],
method: ['deepEqual', 'notDeepEqual'],
arrayBuffer: [0, 1],
method: ['deepEqual', 'notDeepEqual', 'unequal_length'],
}, {
combinationFilter: (p) => {
return p.strict === 1 || p.method === 'deepEqual';
},
});
function main({ len, n, method, strict }) {
const data = Buffer.allocUnsafe(len + 1);
const actual = Buffer.alloc(len);
const expected = Buffer.alloc(len);
const expectedWrong = Buffer.alloc(len + 1);
data.copy(actual);
data.copy(expected);
data.copy(expectedWrong);
function main({ len, n, method, strict, arrayBuffer }) {
let actual = Buffer.alloc(len);
let expected = Buffer.alloc(len + Number(method === 'unequal_length'));
if (method === 'unequal_length') {
method = 'notDeepEqual';
}
for (let i = 0; i < len; i++) {
actual.writeInt8(i % 128, i);
expected.writeInt8(i % 128, i);
}
if (method.includes('not')) {
const position = Math.floor(len / 2);
expected[position] = expected[position] + 1;
}
if (strict) {
method = method.replace('eep', 'eepStrict');
}
const fn = assert[method];
const value2 = method.includes('not') ? expectedWrong : expected;
if (arrayBuffer) {
actual = actual.buffer;
expected = expected.buffer;
}
bench.start();
for (let i = 0; i < n; ++i) {
fn(actual, value2);
fn(actual, expected);
}
bench.end(n);
}

View File

@@ -4,14 +4,20 @@ const common = require('../common.js');
const assert = require('assert');
const bench = common.createBenchmark(main, {
n: [5e3],
size: [1e2, 1e3, 5e4],
strict: [0, 1],
n: [25, 2e2, 2e3],
size: [1e2, 1e3, 1e4],
strict: [1],
method: ['deepEqual', 'notDeepEqual'],
}, {
combinationFilter: (p) => {
return p.size === 1e4 && p.n === 25 ||
p.size === 1e3 && p.n === 2e2 ||
p.size === 1e2 && p.n === 2e3;
},
});
function createObj(source, add = '') {
return source.map((n) => ({
function createObj(size, add = '') {
return Array.from({ length: size }, (n) => ({
foo: 'yarp',
nope: {
bar: `123${add}`,
@@ -24,22 +30,17 @@ function createObj(source, add = '') {
}
function main({ size, n, method, strict }) {
const len = Math.min(Math.ceil(n / size), 20);
const source = Array.apply(null, Array(size));
const actual = createObj(source);
const expected = createObj(source);
const expectedWrong = createObj(source, '4');
if (strict) {
method = method.replace('eep', 'eepStrict');
}
const fn = assert[method];
const value2 = method.includes('not') ? expectedWrong : expected;
const actual = createObj(size);
const expected = method.includes('not') ? createObj(size, '4') : createObj(size);
bench.start();
for (let i = 0; i < len; ++i) {
fn(actual, value2);
for (let i = 0; i < n; ++i) {
fn(actual, expected);
}
bench.end(len);
bench.end(n);
}

View File

@@ -2,35 +2,77 @@
const common = require('../common.js');
const assert = require('assert');
const circular = {};
circular.circular = circular;
const circular2 = {};
circular2.circular = circular2;
const notCircular = {};
notCircular.circular = {};
const primValues = {
'string': 'a',
'number': 1,
'object': { 0: 'a' },
'string': 'abcdef',
'number': 1_000,
'boolean': true,
'object': { property: 'abcdef' },
'object_other_property': { property: 'abcdef' },
'array': [1, 2, 3],
'set_object': new Set([[1]]),
'set_simple': new Set([1, 2, 3]),
'circular': circular,
'empty_object': {},
'regexp': /abc/i,
'date': new Date(),
};
const primValues2 = {
'object': { property: 'abcdef' },
'array': [1, 2, 3],
'set_object': new Set([[1]]),
'set_simple': new Set([1, 3, 2]),
'circular': circular2,
'empty_object': {},
'regexp': /abc/i,
'date': new Date(primValues.date),
};
const primValuesUnequal = {
'string': 'abcdez',
'number': 1_001,
'boolean': false,
'object': { property2: 'abcdef' },
'array': [1, 3, 2],
'set_object': new Set([[2]]),
'set_simple': new Set([1, 4, 2]),
'circular': notCircular,
'empty_object': [],
'regexp': /abc/g,
'date': new Date(primValues.date.getTime() + 1),
};
const bench = common.createBenchmark(main, {
primitive: Object.keys(primValues),
n: [2e4],
n: [1e5],
strict: [0, 1],
method: ['deepEqual', 'notDeepEqual'],
}, {
combinationFilter: (p) => {
return p.strict === 1 || p.method === 'deepEqual';
},
});
function main({ n, primitive, method, strict }) {
const prim = primValues[primitive];
const actual = prim;
const expected = prim;
const expectedWrong = 'b';
const actual = primValues2[primitive] ?? prim;
const expected = method.includes('not') ? primValuesUnequal[primitive] : prim;
if (strict) {
method = method.replace('eep', 'eepStrict');
}
const fn = assert[method];
const value2 = method.includes('not') ? expectedWrong : expected;
bench.start();
for (let i = 0; i < n; ++i) {
fn([actual], [value2]);
fn(actual, expected);
}
bench.end(n);
}

View File

@@ -4,18 +4,10 @@ const common = require('../common.js');
const { deepEqual, deepStrictEqual, notDeepEqual, notDeepStrictEqual } =
require('assert');
const primValues = {
'string': 'a',
'number': 1,
'object': { 0: 'a' },
'array': [1, 2, 3],
};
const bench = common.createBenchmark(main, {
primitive: Object.keys(primValues),
n: [25],
len: [2e4],
strict: [0, 1],
n: [5e2],
len: [1e4],
strict: [1],
method: [
'deepEqual_Array',
'notDeepEqual_Array',
@@ -32,38 +24,32 @@ function run(fn, n, actual, expected) {
bench.end(n);
}
function main({ n, len, primitive, method, strict }) {
const prim = primValues[primitive];
function main({ n, len, method, strict }) {
const actual = [];
const expected = [];
const expectedWrong = [];
for (let x = 0; x < len; x++) {
actual.push(prim);
expected.push(prim);
expectedWrong.push(prim);
for (let i = 0; i < len; i++) {
actual.push(i);
expected.push(i);
}
if (method.includes('not')) {
expected[len - 1] += 1;
}
expectedWrong.pop();
expectedWrong.push('b');
// Note: primitives are only added once to a set
const actualSet = new Set(actual);
const expectedSet = new Set(expected);
const expectedWrongSet = new Set(expectedWrong);
switch (method) {
case 'deepEqual_Array':
run(strict ? deepStrictEqual : deepEqual, n, actual, expected);
break;
case 'notDeepEqual_Array':
run(strict ? notDeepStrictEqual : notDeepEqual, n, actual, expectedWrong);
run(strict ? notDeepStrictEqual : notDeepEqual, n, actual, expected);
break;
case 'deepEqual_Set':
run(strict ? deepStrictEqual : deepEqual, n, actualSet, expectedSet);
run(strict ? deepStrictEqual : deepEqual,
n, new Set(actual), new Set(expected));
break;
case 'notDeepEqual_Set':
run(strict ? notDeepStrictEqual : notDeepEqual,
n, actualSet, expectedWrongSet);
n, new Set(actual), new Set(expected));
break;
default:
throw new Error(`Unsupported method "${method}"`);

View File

@@ -7,8 +7,7 @@ const bench = common.createBenchmark(main, {
'Int8Array',
'Uint8Array',
'Float32Array',
'Float64Array',
'Uint8ClampedArray',
'Uint32Array',
],
n: [5e2],
strict: [0, 1],
@@ -23,21 +22,24 @@ function main({ type, n, len, method, strict }) {
const clazz = global[type];
const actual = new clazz(len);
const expected = new clazz(len);
const expectedWrong = new clazz(len);
const wrongIndex = Math.floor(len / 2);
expectedWrong[wrongIndex] = 123;
if (strict) {
method = method.replace('eep', 'eepStrict');
}
const fn = assert[method];
const value2 = method.includes('not') ? expectedWrong : expected;
if (method.includes('not')) {
expected[Math.floor(len / 2)] = 123;
}
bench.start();
for (let i = 0; i < n; ++i) {
actual[0] = i;
value2[0] = i;
fn(actual, value2);
expected[0] = i;
const pos = Math.ceil(len / 2) + 1;
actual[pos] = i;
expected[pos] = i;
fn(actual, expected);
}
bench.end(n);
}

View File

@@ -1,17 +0,0 @@
'use strict';
const common = require('../common.js');
const assert = require('assert');
const bench = common.createBenchmark(main, { n: [1e5] });
function main({ n }) {
bench.start();
for (let i = 0; i < n; ++i) {
if (i % 2 === 0)
assert(true);
else
assert(true, 'foo bar baz');
}
bench.end(n);
}

View File

@@ -1,42 +0,0 @@
'use strict';
const common = require('../common.js');
const { throws, doesNotThrow } = require('assert');
const bench = common.createBenchmark(main, {
n: [1e4],
method: [ 'doesNotThrow', 'throws_TypeError', 'throws_RegExp' ],
});
function main({ n, method }) {
const throwError = () => { throw new TypeError('foobar'); };
const doNotThrowError = () => { return 'foobar'; };
const regExp = /foobar/;
const message = 'failure';
switch (method) {
case 'doesNotThrow':
bench.start();
for (let i = 0; i < n; ++i) {
doesNotThrow(doNotThrowError);
}
bench.end(n);
break;
case 'throws_TypeError':
bench.start();
for (let i = 0; i < n; ++i) {
throws(throwError, TypeError, message);
}
bench.end(n);
break;
case 'throws_RegExp':
bench.start();
for (let i = 0; i < n; ++i) {
throws(throwError, regExp, message);
}
bench.end(n);
break;
default:
throw new Error(`Unsupported method ${method}`);
}
}