Skip to content

Commit 7a568b4

Browse files
committed
benchmark each module in their own node process
1 parent 693625c commit 7a568b4

File tree

10 files changed

+666
-46
lines changed

10 files changed

+666
-46
lines changed

README.md

+7
Original file line numberDiff line numberDiff line change
@@ -73,3 +73,10 @@ function isMyDataValid(data: any) {
7373
// `res` is now type casted to the right type
7474
const res = isMyDataValid(data)
7575
```
76+
77+
## Local Development
78+
79+
* `npm run start` - run benchmarks for all modules
80+
* `npm run start run zod myzod valita` - run benchmarks only for a few selected modules
81+
* `npm run docs:serve` - result viewer
82+
* `npm run test` - run tests on all modules

benchmarks/helpers/main.ts

+77-12
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { add, complete, cycle, suite } from 'benny';
2-
import { readFileSync, writeFileSync } from 'fs';
2+
import { readFileSync, writeFileSync, existsSync, unlinkSync } from 'fs';
33
import { join } from 'path';
44
import { writePreviewGraph } from './graph';
55
import { getRegisteredBenchmarks } from './register';
@@ -10,15 +10,18 @@ const NODE_VERSION = process.env.NODE_VERSION || process.version;
1010
const NODE_VERSION_FOR_PREVIEW = 17;
1111
const TEST_PREVIEW_GENERATION = false;
1212

13-
export async function main() {
13+
/**
14+
* Run all registered benchmarks and append the results to a file.
15+
*/
16+
export async function runAllBenchmarks() {
1417
if (TEST_PREVIEW_GENERATION) {
15-
// just generate the preview without using benchmark data from a previous run
18+
// during development: generate the preview using benchmark data from a previous run
1619
const allResults: BenchmarkResult[] = JSON.parse(
1720
readFileSync(join(DOCS_DIR, 'results', 'node-17.json')).toString()
1821
).results;
1922

2023
await writePreviewGraph({
21-
filename: join(DOCS_DIR, 'results', 'preview.svg'),
24+
filename: previewSvgFilename(),
2225
values: allResults,
2326
});
2427

@@ -42,24 +45,40 @@ export async function main() {
4245
});
4346
}
4447

45-
writeFileSync(
46-
join(DOCS_DIR, 'results', `node-${majorVersion}.json`),
48+
// collect results of isolated benchmark runs into a single file
49+
appendResults(allResults);
50+
}
4751

48-
JSON.stringify({
49-
results: allResults,
50-
}),
52+
/**
53+
* Remove the results json file.
54+
*/
55+
export function deleteResults() {
56+
const fileName = resultsJsonFilename();
5157

52-
{ encoding: 'utf8' }
53-
);
58+
if (existsSync(fileName)) {
59+
unlinkSync(fileName);
60+
}
61+
}
62+
63+
/**
64+
* Generate the preview svg shown in the readme.
65+
*/
66+
export async function createPreviewGraph() {
67+
const majorVersion = getNodeMajorVersion();
5468

5569
if (majorVersion === NODE_VERSION_FOR_PREVIEW) {
70+
const allResults: BenchmarkResult[] = JSON.parse(
71+
readFileSync(resultsJsonFilename()).toString()
72+
).results;
73+
5674
await writePreviewGraph({
57-
filename: join(DOCS_DIR, 'results', 'preview.svg'),
75+
filename: previewSvgFilename(),
5876
values: allResults,
5977
});
6078
}
6179
}
6280

81+
// run a benchmark fn with benny
6382
async function runBenchmarks(name: string, cases: BenchmarkCase[]) {
6483
const fns = cases.map(c => add(c.moduleName, () => c.run()));
6584

@@ -74,6 +93,52 @@ async function runBenchmarks(name: string, cases: BenchmarkCase[]) {
7493
);
7594
}
7695

96+
// append results to an existing file or create a new one
97+
function appendResults(results: BenchmarkResult[]) {
98+
const fileName = resultsJsonFilename();
99+
const existingResults: BenchmarkResult[] = existsSync(fileName)
100+
? JSON.parse(readFileSync(fileName).toString()).results
101+
: [];
102+
103+
// check that we're appending unique data
104+
const getKey = ({
105+
benchmark,
106+
name,
107+
nodeVersion,
108+
}: BenchmarkResult): string => {
109+
return JSON.stringify({ benchmark, name, nodeVersion });
110+
};
111+
const existingResultsIndex = new Set(existingResults.map(r => getKey(r)));
112+
113+
results.forEach(r => {
114+
if (existingResultsIndex.has(getKey(r))) {
115+
console.error('Result %s already exists in', getKey(r), fileName);
116+
117+
throw new Error('Duplicate result in result json file');
118+
}
119+
});
120+
121+
writeFileSync(
122+
fileName,
123+
124+
JSON.stringify({
125+
results: [...existingResults, ...results],
126+
}),
127+
128+
{ encoding: 'utf8' }
129+
);
130+
}
131+
132+
function resultsJsonFilename() {
133+
const majorVersion = getNodeMajorVersion();
134+
135+
return join(DOCS_DIR, 'results', `node-${majorVersion}.json`);
136+
}
137+
138+
function previewSvgFilename() {
139+
return join(DOCS_DIR, 'results', 'preview.svg');
140+
}
141+
77142
function getNodeMajorVersion() {
78143
let majorVersion = 0;
79144

benchmarks/index.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
export { main } from './helpers/main';
1+
export {
2+
runAllBenchmarks,
3+
createPreviewGraph,
4+
deleteResults,
5+
} from './helpers/main';
26
export {
37
addCase,
48
AvailableBenchmarksIds,

cases/index.ts

+35-27
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,35 @@
1-
import './ajv';
2-
import './bueno';
3-
import './class-validator';
4-
import './computed-types';
5-
import './decoders';
6-
import './io-ts';
7-
import './jointz';
8-
import './json-decoder';
9-
import './marshal';
10-
import './mojotech-json-type-validation';
11-
import './myzod';
12-
import './purify-ts';
13-
import './rulr';
14-
import './runtypes';
15-
import './simple-runtypes';
16-
import './spectypes';
17-
import './superstruct';
18-
import './suretype';
19-
import './toi';
20-
import './tson';
21-
import './ts-interface-checker';
22-
import './ts-json-validator';
23-
import './ts-utils';
24-
import './typeofweb-schema';
25-
import './valita';
26-
import './yup';
27-
import './zod';
1+
export const cases = [
2+
'ajv',
3+
'bueno',
4+
'class-validator',
5+
'computed-types',
6+
'decoders',
7+
'io-ts',
8+
'jointz',
9+
'json-decoder',
10+
'marshal',
11+
'mojotech-json-type-validation',
12+
'myzod',
13+
'purify-ts',
14+
'rulr',
15+
'runtypes',
16+
'simple-runtypes',
17+
'spectypes',
18+
'superstruct',
19+
'suretype',
20+
'toi',
21+
'tson',
22+
'ts-interface-checker',
23+
'ts-json-validator',
24+
'ts-utils',
25+
'typeofweb-schema',
26+
'valita',
27+
'yup',
28+
'zod',
29+
] as const;
30+
31+
export type CaseName = typeof cases[number];
32+
33+
export async function importCase(caseName: CaseName) {
34+
await import('./' + caseName);
35+
}

compiled/spectypes/build/index.d.ts

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
export declare const parseStrict: import("spectypes/dist/dts/types").Spec<["object"], "validator", {
2+
readonly string: string;
3+
readonly number: number;
4+
readonly boolean: boolean;
5+
readonly negNumber: number;
6+
readonly maxNumber: number;
7+
readonly longString: string;
8+
readonly deeplyNested: {
9+
readonly foo: string;
10+
readonly num: number;
11+
readonly bool: boolean;
12+
};
13+
}>;
14+
export declare const parseSafe: import("spectypes/dist/dts/types").Spec<["struct"], "transformer", {
15+
readonly string: string;
16+
readonly number: number;
17+
readonly boolean: boolean;
18+
readonly negNumber: number;
19+
readonly maxNumber: number;
20+
readonly longString: string;
21+
readonly deeplyNested: {
22+
readonly foo: string;
23+
readonly num: number;
24+
readonly bool: boolean;
25+
};
26+
}>;
27+
export declare const assertLoose: import("spectypes/dist/dts/types").Spec<["object-record"], "validator", {
28+
[x: string]: unknown;
29+
readonly string: string;
30+
readonly number: number;
31+
readonly boolean: boolean;
32+
readonly negNumber: number;
33+
readonly maxNumber: number;
34+
readonly longString: string;
35+
readonly deeplyNested: {
36+
[x: string]: unknown;
37+
readonly foo: string;
38+
readonly num: number;
39+
readonly bool: boolean;
40+
};
41+
}>;

0 commit comments

Comments
 (0)