Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 1 addition & 5 deletions benchmark/GraphQLSchema-benchmark.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,5 @@ const bigSchema = buildClientSchema(bigSchemaIntrospectionResult.data);

export const benchmark = {
name: 'Recreate a GraphQLSchema',
count: 40,
measure() {
// eslint-disable-next-line no-new
new GraphQLSchema(bigSchema.toConfig());
},
measure: () => new GraphQLSchema(bigSchema.toConfig()),
};
5 changes: 1 addition & 4 deletions benchmark/buildASTSchema-benchmark.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,5 @@ const schemaAST = parse(bigSchemaSDL);

export const benchmark = {
name: 'Build Schema from AST',
count: 10,
measure() {
buildASTSchema(schemaAST, { assumeValid: true });
},
measure: () => buildASTSchema(schemaAST, { assumeValid: true }),
};
6 changes: 2 additions & 4 deletions benchmark/buildClientSchema-benchmark.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ import { bigSchemaIntrospectionResult } from './fixtures.js';

export const benchmark = {
name: 'Build Schema from Introspection',
count: 10,
measure() {
buildClientSchema(bigSchemaIntrospectionResult.data, { assumeValid: true });
},
measure: () =>
buildClientSchema(bigSchemaIntrospectionResult.data, { assumeValid: true }),
};
5 changes: 1 addition & 4 deletions benchmark/introspectionFromSchema-benchmark.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,5 @@ const document = parse(getIntrospectionQuery());

export const benchmark = {
name: 'Execute Introspection Query',
count: 20,
measure() {
executeSync({ schema, document });
},
measure: () => executeSync({ schema, document }),
};
8 changes: 3 additions & 5 deletions benchmark/list-async-benchmark.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,10 @@ function listField() {

export const benchmark = {
name: 'Execute Asynchronous List Field',
count: 10,
async measure() {
await execute({
measure: () =>
execute({
schema,
document,
rootValue: { listField },
});
},
}),
};
8 changes: 3 additions & 5 deletions benchmark/list-asyncIterable-benchmark.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,10 @@ async function* listField() {

export const benchmark = {
name: 'Execute Async Iterable List Field',
count: 10,
async measure() {
await execute({
measure: () =>
execute({
schema,
document,
rootValue: { listField },
});
},
}),
};
8 changes: 3 additions & 5 deletions benchmark/list-sync-benchmark.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,10 @@ function listField() {

export const benchmark = {
name: 'Execute Synchronous List Field',
count: 10,
async measure() {
await execute({
measure: () =>
execute({
schema,
document,
rootValue: { listField },
});
},
}),
};
5 changes: 1 addition & 4 deletions benchmark/parser-benchmark.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,5 @@ const introspectionQuery = getIntrospectionQuery();

export const benchmark = {
name: 'Parse introspection query',
count: 1000,
measure() {
parse(introspectionQuery);
},
measure: () => parse(introspectionQuery),
};
5 changes: 1 addition & 4 deletions benchmark/printer-benchmark.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,5 @@ const document = parse(bigDocumentSDL);

export const benchmark = {
name: 'Print kitchen sink document',
count: 1000,
measure() {
print(document);
},
measure: () => print(document),
};
5 changes: 1 addition & 4 deletions benchmark/repeated-fields-benchmark.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,5 @@ const source = `{ ${'hello '.repeat(250)}}`;

export const benchmark = {
name: 'Many repeated fields',
count: 5,
measure() {
graphqlSync({ schema, source });
},
measure: () => graphqlSync({ schema, source }),
};
5 changes: 1 addition & 4 deletions benchmark/validateGQL-benchmark.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,5 @@ const queryAST = parse(getIntrospectionQuery());

export const benchmark = {
name: 'Validate Introspection Query',
count: 50,
measure() {
validate(schema, queryAST);
},
measure: () => validate(schema, queryAST),
};
5 changes: 1 addition & 4 deletions benchmark/validateInvalidGQL-benchmark.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,5 @@ const queryAST = parse(`

export const benchmark = {
name: 'Validate Invalid Query',
count: 50,
measure() {
validate(schema, queryAST);
},
measure: () => validate(schema, queryAST),
};
5 changes: 1 addition & 4 deletions benchmark/validateSDL-benchmark.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,5 @@ const sdlAST = parse(bigSchemaSDL);

export const benchmark = {
name: 'Validate SDL Document',
count: 10,
measure() {
validateSDL(sdlAST);
},
measure: () => validateSDL(sdlAST),
};
5 changes: 1 addition & 4 deletions benchmark/visit-benchmark.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,5 @@ const visitor = {

export const benchmark = {
name: 'Visit all AST nodes',
count: 10,
measure() {
visit(documentAST, visitor);
},
measure: () => visit(documentAST, visitor),
};
5 changes: 1 addition & 4 deletions benchmark/visitInParallel-benchmark.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,5 @@ const visitors = new Array(50).fill({

export const benchmark = {
name: 'Visit all AST nodes in parallel',
count: 10,
measure() {
visit(documentAST, visitInParallel(visitors));
},
measure: () => visit(documentAST, visitInParallel(visitors)),
};
1 change: 1 addition & 0 deletions cspell.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ words:
- denoland
- hashbang
- vitest
- mitata
- Rollup
- Rspack
- Rsbuild
Expand Down
8 changes: 8 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
"eslint-plugin-simple-import-sort": "12.1.1",
"husky": "9.1.7",
"lint-staged": "16.2.7",
"mitata": "1.0.34",
"mocha": "11.7.5",
"prettier": "3.8.1",
"ts-node": "10.9.2",
Expand Down
3 changes: 2 additions & 1 deletion resources/benchmark/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@ export const minSamples = 5;

export const memorySamplesPerBenchmark = 10;

export const timingBenchmarkNodeFlags: ReadonlyArray<string> = ['--expose-gc'];

export const memoryBenchmarkNodeFlags: ReadonlyArray<string> = [
'--predictable',
'--no-concurrent-sweeping',
'--no-minor-gc-task',
'--min-semi-space-size=1280', // 1.25GB
'--max-semi-space-size=1280', // 1.25GB
'--trace-gc', // no gc calls should happen during benchmark, so trace them
];
25 changes: 3 additions & 22 deletions resources/benchmark/sampling.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,17 @@
import assert from 'node:assert';

import { maxTime, memorySamplesPerBenchmark, minSamples } from './config.js';
import { yellow } from './output.js';
import type { BenchmarkTimingSample } from './types.js';
import { sampleMemoryModule, sampleTimingModule } from './workers.js';

export function collectTimingSamples(
modulePath: string,
): Array<BenchmarkTimingSample> {
let numOfConsequentlyRejectedSamples = 0;
const samples: Array<BenchmarkTimingSample> = [];
export function collectTimingSamples(modulePath: string): Array<number> {
const samples: Array<number> = [];

// If time permits, increase sample size to reduce the margin of error.
const start = Date.now();
while (samples.length < minSamples || (Date.now() - start) / 1e3 < maxTime) {
const sample = sampleTimingModule(modulePath);

if (sample.involuntaryContextSwitches > 0) {
numOfConsequentlyRejectedSamples++;
if (numOfConsequentlyRejectedSamples === 5) {
console.error(
yellow(
' Five or more consequent runs beings rejected because of context switching.\n' +
' Measurement can take a significantly longer time and its correctness can also be impacted.',
),
);
}
continue;
}
numOfConsequentlyRejectedSamples = 0;

assert(sample.clocked > 0);
assert(sample > 0);
samples.push(sample);
}
return samples;
Expand Down
8 changes: 4 additions & 4 deletions resources/benchmark/statistics.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import assert from 'node:assert';

import { NS_PER_SEC } from './config.js';
import type { BenchmarkResult, BenchmarkTimingSample } from './types.js';
import type { BenchmarkResult } from './types.js';

// T-Distribution two-tailed critical values for 95% confidence.
// See http://www.itl.nist.gov/div898/handbook/eda/section3/eda3672.htm.
Expand All @@ -18,15 +18,15 @@ const tTableInfinity = 1.96;
// Computes stats on benchmark results.
export function computeStats(
name: string,
timingSamples: ReadonlyArray<BenchmarkTimingSample>,
timingSamples: ReadonlyArray<number>,
memorySamples: ReadonlyArray<number>,
): BenchmarkResult {
assert(timingSamples.length > 1);
assert(memorySamples.length > 0);

// Compute the sample mean (estimate of the population mean).
let mean = 0;
for (const { clocked } of timingSamples) {
for (const clocked of timingSamples) {
mean += clocked;
}
mean /= timingSamples.length;
Expand All @@ -39,7 +39,7 @@ export function computeStats(

// Compute the sample variance (estimate of the population variance).
let variance = 0;
for (const { clocked } of timingSamples) {
for (const clocked of timingSamples) {
variance += (clocked - mean) ** 2;
}
variance /= timingSamples.length - 1;
Expand Down
5 changes: 0 additions & 5 deletions resources/benchmark/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,6 @@ export interface BenchmarkProject {
projectPath: string;
}

export interface BenchmarkTimingSample {
clocked: number;
involuntaryContextSwitches: number;
}

export interface BenchmarkResult {
name: string;
memPerOp: number;
Expand Down
31 changes: 17 additions & 14 deletions resources/benchmark/worker-memory.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,28 @@ import {
writeResult,
} from './worker-utils.js';

const memoryWarmupIterations = 10;
const memorySampleIterations = 10;

runWorker(async () => {
const benchmark = await loadBenchmark(readModulePath());
await warmUp(benchmark);
writeResult(await measureMemoryUsage(benchmark));
});

async function measureMemoryUsage(benchmark) {
await runIterations(benchmark, memoryWarmupIterations);

const memBaseline = process.memoryUsage().heapUsed;
for (let i = 0; i < benchmark.count; ++i) {
await runIterations(benchmark, memorySampleIterations);
return (
(process.memoryUsage().heapUsed - memBaseline) / memorySampleIterations
);
}

async function runIterations(benchmark, iterations) {
for (let i = 0; i < iterations; ++i) {
// Each benchmark decides whether the measurement must await async work.
// eslint-disable-next-line no-await-in-loop
await benchmark.measure();
}
writeResult((process.memoryUsage().heapUsed - memBaseline) / benchmark.count);
});

async function warmUp(benchmark) {
// It looks like 7 is a magic number to reliably trigger JIT.
await benchmark.measure();
await benchmark.measure();
await benchmark.measure();
await benchmark.measure();
await benchmark.measure();
await benchmark.measure();
await benchmark.measure();
}
34 changes: 7 additions & 27 deletions resources/benchmark/worker-timing.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import assert from 'node:assert';

import { measure } from 'mitata';

import {
loadBenchmark,
readModulePath,
Expand All @@ -7,32 +11,8 @@ import {

runWorker(async () => {
const benchmark = await loadBenchmark(readModulePath());
await warmUp(benchmark);
assert(globalThis.gc != null);

const resourcesStart = process.resourceUsage();
const startTime = process.hrtime.bigint();
for (let i = 0; i < benchmark.count; ++i) {
// eslint-disable-next-line no-await-in-loop
await benchmark.measure();
}
const timeDiff = Number(process.hrtime.bigint() - startTime);
const resourcesEnd = process.resourceUsage();

writeResult({
clocked: timeDiff / benchmark.count,
involuntaryContextSwitches:
resourcesEnd.involuntaryContextSwitches -
resourcesStart.involuntaryContextSwitches,
});
const timingStats = await measure(benchmark.measure);
writeResult(timingStats.avg);
});

async function warmUp(benchmark) {
// It looks like 7 is a magic number to reliably trigger JIT.
await benchmark.measure();
await benchmark.measure();
await benchmark.measure();
await benchmark.measure();
await benchmark.measure();
await benchmark.measure();
await benchmark.measure();
}
Loading
Loading