lib: improve performance of validateStringArray and validateBooleanArray

PR-URL: https://github.com/nodejs/node/pull/49756
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
This commit is contained in:
Aras Abbasi 2023-10-22 23:12:38 +02:00 committed by GitHub
parent d370ed0cd8
commit a58ffad656
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 390 additions and 5 deletions

View File

@ -0,0 +1,61 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const bench = common.createBenchmark(main, {
n: [1e7],
value: [
"'777'",
'0o777',
],
}, {
flags: ['--expose-internals'],
});
function getParseFactory() {
const {
parseFileMode,
} = require('internal/validators');
return (n) => parseFileMode(n, 'n');
}
function main({ n, value }) {
const parse = getParseFactory();
value = value === "'777'" ? '777' : 0o777;
// Warm up.
const length = 1024;
const array = [];
let errCase = false;
for (let i = 0; i < length; ++i) {
try {
array.push(parse(value));
} catch (e) {
errCase = true;
array.push(e);
}
}
bench.start();
for (let i = 0; i < n; ++i) {
const index = i % length;
try {
array[index] = parse(value);
} catch (e) {
array[index] = e;
}
}
bench.end(n);
// Verify the entries to prevent dead code elimination from making
// the benchmark invalid.
for (let i = 0; i < length; ++i) {
assert.strictEqual(typeof array[i], errCase ? 'object' : 'number');
}
}

View File

@ -0,0 +1,68 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const bench = common.createBenchmark(main, {
n: [1e7],
value: [
'[]',
'[1,2,3]',
],
}, {
flags: ['--expose-internals'],
});
function getValidateFactory() {
const {
validateArray,
} = require('internal/validators');
return (n) => validateArray(n, 'n');
}
function main({ n, value }) {
const validate = getValidateFactory();
switch (value) {
case '[]':
value = [];
break;
case '[1,2,3]':
value = [1, 2, 3];
break;
}
// Warm up.
const length = 1024;
const array = [];
let errCase = false;
for (let i = 0; i < length; ++i) {
try {
array.push(validate(value));
} catch (e) {
errCase = true;
array.push(e);
}
}
bench.start();
for (let i = 0; i < n; ++i) {
const index = i % length;
try {
array[index] = validate(value);
} catch (e) {
array[index] = e;
}
}
bench.end(n);
// Verify the entries to prevent dead code elimination from making
// the benchmark invalid.
for (let i = 0; i < length; ++i) {
assert.strictEqual(typeof array[i], errCase ? 'object' : 'undefined');
}
}

View File

@ -0,0 +1,55 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const bench = common.createBenchmark(main, {
n: [1e8],
code: [
'validateBoolean',
],
value: [
'true',
'false',
],
}, {
flags: ['--expose-internals'],
});
function getValidateFactory(code) {
const {
validateBoolean,
} = require('internal/validators');
switch (code) {
case 'validateBoolean':
return (n) => validateBoolean(n, 'n');
}
}
function main({ n, code, value }) {
const validate = getValidateFactory(code);
const v = value === 'true';
// Warm up.
const length = 1024;
const array = [];
for (let i = 0; i < length; ++i) {
array.push(validate(v));
}
bench.start();
for (let i = 0; i < n; ++i) {
const index = i % length;
array[index] = validate(v);
}
bench.end(n);
// Verify the entries to prevent dead code elimination from making
// the benchmark invalid.
for (let i = 0; i < length; ++i) {
assert.strictEqual(typeof array[i], 'undefined');
}
}

View File

@ -0,0 +1,59 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const bench = common.createBenchmark(main, {
n: [1e8],
encoding: [
'ascii',
'utf8',
'utf-8',
'utf16le',
'ucs2',
'ucs-2',
'base64',
'latin1',
'binary',
'hex',
],
value: [
'test',
],
}, {
flags: ['--expose-internals'],
});
function getValidateFactory(encoding) {
const {
validateEncoding,
} = require('internal/validators');
return (n) => validateEncoding(n, encoding);
}
function main({ n, encoding, value }) {
const validate = getValidateFactory(encoding);
// Warm up.
const length = 1024;
const array = [];
for (let i = 0; i < length; ++i) {
array.push(validate(value));
}
bench.start();
for (let i = 0; i < n; ++i) {
const index = i % length;
array[index] = validate(value);
}
bench.end(n);
// Verify the entries to prevent dead code elimination from making
// the benchmark invalid.
for (let i = 0; i < length; ++i) {
assert.strictEqual(typeof array[i], 'undefined');
}
}

View File

@ -0,0 +1,69 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const bench = common.createBenchmark(main, {
n: [1e7],
code: [
'validateOneOf',
],
value: [
'fifo',
'lifo',
'lilo',
],
validLength: [
1,
2,
3,
],
}, {
flags: ['--expose-internals'],
});
const validValues = [
'fifo',
'lifo',
'lilo',
'filo',
];
function getValidateFactory(code, validLength) {
const {
validateOneOf,
} = require('internal/validators');
switch (code) {
case 'validateOneOf':
return (n) => validateOneOf(n, 'n', validValues.slice(0, validLength));
}
}
function main({ n, code, validLength }) {
const validate = getValidateFactory(code, validLength);
// Warm up.
const length = 1024;
const array = [];
const value = validValues[validLength - 1];
for (let i = 0; i < length; ++i) {
array.push(validate(value));
}
bench.start();
for (let i = 0; i < n; ++i) {
const index = i % length;
array[index] = validate(value);
}
bench.end(n);
// Verify the entries to prevent dead code elimination from making
// the benchmark invalid.
for (let i = 0; i < length; ++i) {
assert.strictEqual(typeof array[i], 'undefined');
}
}

View File

@ -0,0 +1,66 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const bench = common.createBenchmark(main, {
n: [1e8],
type: [
'validateStringArray',
'validateBooleanArray',
],
arrayLength: [
0,
1,
10,
100,
],
}, {
flags: ['--expose-internals'],
});
function getValidateFactory(type, arrayLength) {
const {
validateBooleanArray,
validateStringArray,
} = require('internal/validators');
switch (type) {
case 'validateBooleanArray':
return [
(n) => validateBooleanArray(n, 'n'),
Array.from({ length: arrayLength }, (v, i) => ((i & 1) === 0)),
];
case 'validateStringArray':
return [
(n) => validateStringArray(n, 'n'),
Array.from({ length: arrayLength }, (v, i) => `foo${i}`),
];
}
}
function main({ n, type, arrayLength }) {
const [validate, value] = getValidateFactory(type, arrayLength);
// Warm up.
const length = 1024;
const array = [];
for (let i = 0; i < length; ++i) {
array.push(validate(value));
}
bench.start();
for (let i = 0; i < n; ++i) {
const index = i % length;
array[index] = validate(value);
}
bench.end(n);
// Verify the entries to prevent dead code elimination from making
// the benchmark invalid.
for (let i = 0; i < length; ++i) {
assert.strictEqual(typeof array[i], 'undefined');
}
}

View File

@ -311,8 +311,12 @@ const validateArray = hideStackFrames((value, name, minLength = 0) => {
/** @type {validateStringArray} */
function validateStringArray(value, name) {
validateArray(value, name);
for (let i = 0; i < value.length; i++) {
validateString(value[i], `${name}[${i}]`);
for (let i = 0; i < value.length; ++i) {
// Don't use validateString here for performance reasons, as
// we would generate intermediate strings for the name.
if (typeof value[i] !== 'string') {
throw new ERR_INVALID_ARG_TYPE(`${name}[${i}]`, 'string', value[i]);
}
}
}
@ -326,8 +330,12 @@ function validateStringArray(value, name) {
/** @type {validateBooleanArray} */
function validateBooleanArray(value, name) {
validateArray(value, name);
for (let i = 0; i < value.length; i++) {
validateBoolean(value[i], `${name}[${i}]`);
for (let i = 0; i < value.length; ++i) {
// Don't use validateBoolean here for performance reasons, as
// we would generate intermediate strings for the name.
if (value[i] !== true && value[i] !== false) {
throw new ERR_INVALID_ARG_TYPE(`${name}[${i}]`, 'boolean', value[i]);
}
}
}

View File

@ -4,7 +4,6 @@ require('../common');
// Minimal test for assert benchmarks. This makes sure the benchmarks aren't
// completely broken but nothing more than that.
const runBenchmark = require('../common/benchmark');
runBenchmark('validators');