overtake 1.0.4 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -19,10 +19,11 @@ const suite = benchmark('1M numbers', () => Array.from({ length: 1e6 }, (_, i) =
19
19
  suite.target('for loop').measure('sum', (_, arr) => {
20
20
  let sum = 0;
21
21
  for (let i = 0; i < arr.length; i++) sum += arr[i];
22
- return sum;
23
22
  });
24
23
 
25
- suite.target('reduce').measure('sum', (_, arr) => arr.reduce((a, b) => a + b));
24
+ suite.target('reduce').measure('sum', (_, arr) => {
25
+ arr.reduce((a, b) => a + b);
26
+ });
26
27
  ```
27
28
 
28
29
  ```bash
@@ -85,9 +86,36 @@ benchmark('data', getData)
85
86
  const { serialize } = await import('node:v8');
86
87
  return { serialize };
87
88
  })
88
- .measure('serialize', ({ serialize }, input) => serialize(input));
89
+ .measure('serialize', ({ serialize }, input) => {
90
+ serialize(input);
91
+ });
89
92
  ```
90
93
 
94
+ ### Importing Local Files
95
+
96
+ **Important**: Dynamic imports inside target functions resolve relative to the worker location (`node_modules/overtake/build/worker.js`), not your project root. For local files, you must construct absolute paths:
97
+
98
+ ````typescript
99
+ // ❌ WRONG - Relative path resolves from worker location
100
+ .target('myImpl', async () => {
101
+ const { myFunc } = await import('./utils/myModule.js'); // Error: looks in node_modules/overtake/build/utils/
102
+ return { myFunc };
103
+ })
104
+
105
+ // ✅ CORRECT - Build absolute path inside the function
106
+ .target('myImpl', async () => {
107
+ const { join } = await import('node:path');
108
+ const modulePath = join(process.cwd(), './utils/myModule.js');
109
+ const { myFunc } = await import(modulePath);
110
+ return { myFunc };
111
+ })
112
+
113
+ // ✅ ALSO CORRECT - For node_modules packages
114
+ .target('lib', async () => {
115
+ const { someFunc } = await import('my-package');
116
+ return { someFunc };
117
+ })
118
+
91
119
  ## Usage
92
120
 
93
121
  ### CLI Mode (Recommended)
@@ -99,12 +127,14 @@ When using `npx overtake`, a global `benchmark` function is provided:
99
127
  benchmark('small', () => generateSmallData())
100
128
  .feed('large', () => generateLargeData())
101
129
  .target('algorithm A')
102
- .measure('process', (_, input) => processA(input))
130
+ .measure('process', (_, input) => {
131
+ processA(input);
132
+ })
103
133
  .target('algorithm B')
104
- .measure('process', (_, input) => processB(input));
105
-
106
- // No .execute() needed - CLI handles it
107
- ```
134
+ .measure('process', (_, input) => {
135
+ processB(input);
136
+ });
137
+ ````
108
138
 
109
139
  ```bash
110
140
  npx overtake benchmark.ts --format table
@@ -119,7 +149,9 @@ import { Benchmark, printTableReports } from 'overtake';
119
149
 
120
150
  const suite = new Benchmark('dataset', () => getData());
121
151
 
122
- suite.target('impl').measure('op', (_, input) => process(input));
152
+ suite.target('impl').measure('op', (_, input) => {
153
+ process(input);
154
+ });
123
155
 
124
156
  // Must explicitly execute
125
157
  const reports = await suite.execute({
@@ -167,7 +199,6 @@ suite
167
199
  // ctx contains setup return value
168
200
  const hash = createHash('sha256').update(input).digest();
169
201
  cache.set(input, hash);
170
- return hash;
171
202
  });
172
203
  ```
173
204
 
@@ -184,29 +215,45 @@ suite
184
215
  .measure('process', ({ gcBlock }, input) => {
185
216
  const result = input.map((x) => x * x);
186
217
  gcBlock.add(result); // Prevent GC during measurement
187
- return result;
188
218
  });
189
219
  ```
190
220
 
191
221
  ## Examples
192
222
 
193
- ### Serialization Comparison
223
+ ### Compare Algorithms
194
224
 
195
225
  ```typescript
196
- // Compare V8 vs JSON serialization
197
- const suite = benchmark('10K strings', () => Array.from({ length: 10_000 }, (_, i) => `string-${i}`));
226
+ // examples/quick-start.ts
227
+ const sumBenchmark = benchmark('1M numbers', () => Array.from({ length: 1_000_000 }, (_, i) => i));
198
228
 
199
- suite
200
- .target('V8', async () => {
201
- const { serialize } = await import('node:v8');
202
- return { serialize };
203
- })
204
- .measure('serialize', ({ serialize }, input) => serialize(input));
229
+ sumBenchmark.target('for loop').measure('sum', (_, numbers) => {
230
+ let sum = 0;
231
+ for (let i = 0; i < numbers.length; i++) sum += numbers[i];
232
+ });
205
233
 
206
- suite.target('JSON').measure('serialize', (_, input) => JSON.stringify(input));
234
+ sumBenchmark.target('reduce').measure('sum', (_, numbers) => {
235
+ numbers.reduce((a, b) => a + b, 0);
236
+ });
237
+ ```
238
+
239
+ ### Import Local Modules
240
+
241
+ ```typescript
242
+ // examples/imports.ts - Correct way to import local files
243
+ .target('local files', async () => {
244
+ const { join } = await import('node:path');
245
+ const modulePath = join(process.cwd(), './build/myModule.js');
246
+ const { myFunction } = await import(modulePath);
247
+ return { myFunction };
248
+ })
207
249
  ```
208
250
 
209
- **[📁 More examples in `/examples`](./examples/)**
251
+ **[📁 See all examples](./examples/):**
252
+
253
+ - `quick-start.ts` - Minimal benchmark example
254
+ - `complete.ts` - All features (setup/teardown, pre/post hooks, multiple feeds)
255
+ - `imports.ts` - Import patterns and memory management
256
+ - `custom-reports.ts` - Statistics and custom report types
210
257
 
211
258
  ## CLI Options
212
259
 
package/bin/overtake.js CHANGED
@@ -1,2 +1,2 @@
1
- #!/usr/bin/env -S node --experimental-vm-modules --no-warnings
1
+ #!/usr/bin/env -S node --experimental-vm-modules --no-warnings --expose-gc
2
2
  import '../build/cli.js';
package/build/cli.cjs CHANGED
@@ -96,7 +96,8 @@ commander.name(name).description(description).version(version).argument('<path>'
96
96
  };
97
97
  const script = new _nodevm.SourceTextModule(code, {
98
98
  context: (0, _nodevm.createContext)({
99
- benchmark
99
+ benchmark,
100
+ Buffer
100
101
  })
101
102
  });
102
103
  const imports = new Map();
package/build/cli.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli.ts"],"sourcesContent":["import { createRequire, Module } from 'node:module';\nimport { SyntheticModule, createContext, SourceTextModule } from 'node:vm';\nimport { stat, readFile } from 'node:fs/promises';\nimport { transform } from '@swc/core';\nimport { Command, Option } from 'commander';\nimport { glob } from 'glob';\nimport { Benchmark, printTableReports, printJSONReports, printSimpleReports, DEFAULT_REPORT_TYPES, DEFAULT_WORKERS } from './index.js';\nimport { REPORT_TYPES } from './types.js';\n\nconst require = createRequire(import.meta.url);\nconst { name, description, version } = require('../package.json');\n\nconst commander = new Command();\n\nconst transpile = async (code: string): Promise<string> => {\n const output = await transform(code, {\n filename: 'benchmark.ts',\n jsc: {\n parser: {\n syntax: 'typescript',\n tsx: false,\n dynamicImport: true,\n },\n target: 'esnext',\n },\n module: {\n type: 'es6',\n },\n });\n return output.code;\n};\n\ncommander\n .name(name)\n .description(description)\n .version(version)\n .argument('<path>', 'glob pattern to find benchmarks')\n .addOption(new Option('-r, --report-types [reportTypes...]', 'statistic types to include in the report').choices(REPORT_TYPES).default(DEFAULT_REPORT_TYPES))\n .addOption(new Option('-w, --workers [workers]', 'number of concurent workers').default(DEFAULT_WORKERS).argParser(parseInt))\n .addOption(new Option('-f, --format [format]', 'output format').default('simple').choices(['simple', 'json', 'pjson', 'table']))\n .addOption(new Option('--abs-threshold [absThreshold]', 'absolute error threshold in nanoseconds').argParser(parseInt))\n .addOption(new Option('--rel-threshold [relThreshold]', 'relative error threshold (fraction between 0 and 1)').argParser(parseInt))\n .addOption(new Option('--warmup-cycles [warmupCycles]', 'number of warmup cycles before measuring').argParser(parseInt))\n .addOption(new Option('--max-cycles [maxCycles]', 'maximum measurement cycles per feed').argParser(parseInt))\n .addOption(new Option('--min-cycles [minCycles]', 'minimum measurement cycles per feed').argParser(parseInt))\n .action(async (path, executeOptions) => {\n const files = await glob(path, { absolute: true, cwd: process.cwd() }).catch(() => []);\n for (const file of files) {\n const stats = await stat(file).catch(() => false as const);\n if (stats && stats.isFile()) {\n const content = await readFile(file, 'utf8');\n const code = await transpile(content);\n let instance: Benchmark<unknown> | undefined;\n const benchmark = (...args: Parameters<(typeof Benchmark)['create']>) => {\n if (instance) {\n throw new Error('Only one benchmark per file is supported');\n }\n instance = Benchmark.create(...args);\n return instance;\n };\n const script = new SourceTextModule(code, {\n context: createContext({ benchmark }),\n });\n const imports = new Map();\n await script.link(async (specifier: string, referencingModule) => {\n if (imports.has(specifier)) {\n return imports.get(specifier);\n }\n const mod = await import(Module.isBuiltin(specifier) ? specifier : require.resolve(specifier));\n const exportNames = Object.keys(mod);\n const imported = new SyntheticModule(\n exportNames,\n () => {\n exportNames.forEach((key) => imported.setExport(key, mod[key]));\n },\n { identifier: specifier, context: referencingModule.context },\n );\n\n imports.set(specifier, imported);\n return imported;\n });\n await script.evaluate();\n\n if (instance) {\n const reports = await instance.execute(executeOptions);\n switch (executeOptions.format) {\n case 'json':\n {\n printJSONReports(reports);\n }\n break;\n case 'pjson':\n {\n printJSONReports(reports, 2);\n }\n break;\n case 'table':\n {\n printTableReports(reports);\n }\n break;\n default:\n printSimpleReports(reports);\n }\n }\n }\n }\n });\n\ncommander.parse(process.argv);\n"],"names":["require","createRequire","name","description","version","commander","Command","transpile","code","output","transform","filename","jsc","parser","syntax","tsx","dynamicImport","target","module","type","argument","addOption","Option","choices","REPORT_TYPES","default","DEFAULT_REPORT_TYPES","DEFAULT_WORKERS","argParser","parseInt","action","path","executeOptions","files","glob","absolute","cwd","process","catch","file","stats","stat","isFile","content","readFile","instance","benchmark","args","Error","Benchmark","create","script","SourceTextModule","context","createContext","imports","Map","link","specifier","referencingModule","has","get","mod","Module","isBuiltin","resolve","exportNames","Object","keys","imported","SyntheticModule","forEach","key","setExport","identifier","set","evaluate","reports","execute","format","printJSONReports","printTableReports","printSimpleReports","parse","argv"],"mappings":"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"}
1
+ {"version":3,"sources":["../src/cli.ts"],"sourcesContent":["import { createRequire, Module } from 'node:module';\nimport { SyntheticModule, createContext, SourceTextModule } from 'node:vm';\nimport { stat, readFile } from 'node:fs/promises';\nimport { transform } from '@swc/core';\nimport { Command, Option } from 'commander';\nimport { glob } from 'glob';\nimport { Benchmark, printTableReports, printJSONReports, printSimpleReports, DEFAULT_REPORT_TYPES, DEFAULT_WORKERS } from './index.js';\nimport { REPORT_TYPES } from './types.js';\n\nconst require = createRequire(import.meta.url);\nconst { name, description, version } = require('../package.json');\n\nconst commander = new Command();\n\nconst transpile = async (code: string): Promise<string> => {\n const output = await transform(code, {\n filename: 'benchmark.ts',\n jsc: {\n parser: {\n syntax: 'typescript',\n tsx: false,\n dynamicImport: true,\n },\n target: 'esnext',\n },\n module: {\n type: 'es6',\n },\n });\n return output.code;\n};\n\ncommander\n .name(name)\n .description(description)\n .version(version)\n .argument('<path>', 'glob pattern to find benchmarks')\n .addOption(new Option('-r, --report-types [reportTypes...]', 'statistic types to include in the report').choices(REPORT_TYPES).default(DEFAULT_REPORT_TYPES))\n .addOption(new Option('-w, --workers [workers]', 'number of concurent workers').default(DEFAULT_WORKERS).argParser(parseInt))\n .addOption(new Option('-f, --format [format]', 'output format').default('simple').choices(['simple', 'json', 'pjson', 'table']))\n .addOption(new Option('--abs-threshold [absThreshold]', 'absolute error threshold in nanoseconds').argParser(parseInt))\n .addOption(new Option('--rel-threshold [relThreshold]', 'relative error threshold (fraction between 0 and 1)').argParser(parseInt))\n .addOption(new Option('--warmup-cycles [warmupCycles]', 'number of warmup cycles before measuring').argParser(parseInt))\n .addOption(new Option('--max-cycles [maxCycles]', 'maximum measurement cycles per feed').argParser(parseInt))\n .addOption(new Option('--min-cycles [minCycles]', 'minimum measurement cycles per feed').argParser(parseInt))\n .action(async (path, executeOptions) => {\n const files = await glob(path, { absolute: true, cwd: process.cwd() }).catch(() => []);\n for (const file of files) {\n const stats = await stat(file).catch(() => false as const);\n if (stats && stats.isFile()) {\n const content = await readFile(file, 'utf8');\n const code = await transpile(content);\n let instance: Benchmark<unknown> | undefined;\n const benchmark = (...args: Parameters<(typeof Benchmark)['create']>) => {\n if (instance) {\n throw new Error('Only one benchmark per file is supported');\n }\n instance = Benchmark.create(...args);\n return instance;\n };\n const script = new SourceTextModule(code, {\n context: createContext({\n benchmark,\n Buffer,\n }),\n });\n const imports = new Map();\n await script.link(async (specifier: string, referencingModule) => {\n if (imports.has(specifier)) {\n return imports.get(specifier);\n }\n const mod = await import(Module.isBuiltin(specifier) ? specifier : require.resolve(specifier));\n const exportNames = Object.keys(mod);\n const imported = new SyntheticModule(\n exportNames,\n () => {\n exportNames.forEach((key) => imported.setExport(key, mod[key]));\n },\n { identifier: specifier, context: referencingModule.context },\n );\n\n imports.set(specifier, imported);\n return imported;\n });\n await script.evaluate();\n\n if (instance) {\n const reports = await instance.execute(executeOptions);\n switch (executeOptions.format) {\n case 'json':\n {\n printJSONReports(reports);\n }\n break;\n case 'pjson':\n {\n printJSONReports(reports, 2);\n }\n break;\n case 'table':\n {\n printTableReports(reports);\n }\n break;\n default:\n printSimpleReports(reports);\n }\n }\n }\n }\n });\n\ncommander.parse(process.argv);\n"],"names":["require","createRequire","name","description","version","commander","Command","transpile","code","output","transform","filename","jsc","parser","syntax","tsx","dynamicImport","target","module","type","argument","addOption","Option","choices","REPORT_TYPES","default","DEFAULT_REPORT_TYPES","DEFAULT_WORKERS","argParser","parseInt","action","path","executeOptions","files","glob","absolute","cwd","process","catch","file","stats","stat","isFile","content","readFile","instance","benchmark","args","Error","Benchmark","create","script","SourceTextModule","context","createContext","Buffer","imports","Map","link","specifier","referencingModule","has","get","mod","Module","isBuiltin","resolve","exportNames","Object","keys","imported","SyntheticModule","forEach","key","setExport","identifier","set","evaluate","reports","execute","format","printJSONReports","printTableReports","printSimpleReports","parse","argv"],"mappings":"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"}
package/build/cli.js CHANGED
@@ -51,7 +51,8 @@ commander.name(name).description(description).version(version).argument('<path>'
51
51
  };
52
52
  const script = new SourceTextModule(code, {
53
53
  context: createContext({
54
- benchmark
54
+ benchmark,
55
+ Buffer
55
56
  })
56
57
  });
57
58
  const imports = new Map();
package/build/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli.ts"],"sourcesContent":["import { createRequire, Module } from 'node:module';\nimport { SyntheticModule, createContext, SourceTextModule } from 'node:vm';\nimport { stat, readFile } from 'node:fs/promises';\nimport { transform } from '@swc/core';\nimport { Command, Option } from 'commander';\nimport { glob } from 'glob';\nimport { Benchmark, printTableReports, printJSONReports, printSimpleReports, DEFAULT_REPORT_TYPES, DEFAULT_WORKERS } from './index.js';\nimport { REPORT_TYPES } from './types.js';\n\nconst require = createRequire(import.meta.url);\nconst { name, description, version } = require('../package.json');\n\nconst commander = new Command();\n\nconst transpile = async (code: string): Promise<string> => {\n const output = await transform(code, {\n filename: 'benchmark.ts',\n jsc: {\n parser: {\n syntax: 'typescript',\n tsx: false,\n dynamicImport: true,\n },\n target: 'esnext',\n },\n module: {\n type: 'es6',\n },\n });\n return output.code;\n};\n\ncommander\n .name(name)\n .description(description)\n .version(version)\n .argument('<path>', 'glob pattern to find benchmarks')\n .addOption(new Option('-r, --report-types [reportTypes...]', 'statistic types to include in the report').choices(REPORT_TYPES).default(DEFAULT_REPORT_TYPES))\n .addOption(new Option('-w, --workers [workers]', 'number of concurent workers').default(DEFAULT_WORKERS).argParser(parseInt))\n .addOption(new Option('-f, --format [format]', 'output format').default('simple').choices(['simple', 'json', 'pjson', 'table']))\n .addOption(new Option('--abs-threshold [absThreshold]', 'absolute error threshold in nanoseconds').argParser(parseInt))\n .addOption(new Option('--rel-threshold [relThreshold]', 'relative error threshold (fraction between 0 and 1)').argParser(parseInt))\n .addOption(new Option('--warmup-cycles [warmupCycles]', 'number of warmup cycles before measuring').argParser(parseInt))\n .addOption(new Option('--max-cycles [maxCycles]', 'maximum measurement cycles per feed').argParser(parseInt))\n .addOption(new Option('--min-cycles [minCycles]', 'minimum measurement cycles per feed').argParser(parseInt))\n .action(async (path, executeOptions) => {\n const files = await glob(path, { absolute: true, cwd: process.cwd() }).catch(() => []);\n for (const file of files) {\n const stats = await stat(file).catch(() => false as const);\n if (stats && stats.isFile()) {\n const content = await readFile(file, 'utf8');\n const code = await transpile(content);\n let instance: Benchmark<unknown> | undefined;\n const benchmark = (...args: Parameters<(typeof Benchmark)['create']>) => {\n if (instance) {\n throw new Error('Only one benchmark per file is supported');\n }\n instance = Benchmark.create(...args);\n return instance;\n };\n const script = new SourceTextModule(code, {\n context: createContext({ benchmark }),\n });\n const imports = new Map();\n await script.link(async (specifier: string, referencingModule) => {\n if (imports.has(specifier)) {\n return imports.get(specifier);\n }\n const mod = await import(Module.isBuiltin(specifier) ? specifier : require.resolve(specifier));\n const exportNames = Object.keys(mod);\n const imported = new SyntheticModule(\n exportNames,\n () => {\n exportNames.forEach((key) => imported.setExport(key, mod[key]));\n },\n { identifier: specifier, context: referencingModule.context },\n );\n\n imports.set(specifier, imported);\n return imported;\n });\n await script.evaluate();\n\n if (instance) {\n const reports = await instance.execute(executeOptions);\n switch (executeOptions.format) {\n case 'json':\n {\n printJSONReports(reports);\n }\n break;\n case 'pjson':\n {\n printJSONReports(reports, 2);\n }\n break;\n case 'table':\n {\n printTableReports(reports);\n }\n break;\n default:\n printSimpleReports(reports);\n }\n }\n }\n }\n });\n\ncommander.parse(process.argv);\n"],"names":["createRequire","Module","SyntheticModule","createContext","SourceTextModule","stat","readFile","transform","Command","Option","glob","Benchmark","printTableReports","printJSONReports","printSimpleReports","DEFAULT_REPORT_TYPES","DEFAULT_WORKERS","REPORT_TYPES","require","url","name","description","version","commander","transpile","code","output","filename","jsc","parser","syntax","tsx","dynamicImport","target","module","type","argument","addOption","choices","default","argParser","parseInt","action","path","executeOptions","files","absolute","cwd","process","catch","file","stats","isFile","content","instance","benchmark","args","Error","create","script","context","imports","Map","link","specifier","referencingModule","has","get","mod","isBuiltin","resolve","exportNames","Object","keys","imported","forEach","key","setExport","identifier","set","evaluate","reports","execute","format","parse","argv"],"mappings":"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"}
1
+ {"version":3,"sources":["../src/cli.ts"],"sourcesContent":["import { createRequire, Module } from 'node:module';\nimport { SyntheticModule, createContext, SourceTextModule } from 'node:vm';\nimport { stat, readFile } from 'node:fs/promises';\nimport { transform } from '@swc/core';\nimport { Command, Option } from 'commander';\nimport { glob } from 'glob';\nimport { Benchmark, printTableReports, printJSONReports, printSimpleReports, DEFAULT_REPORT_TYPES, DEFAULT_WORKERS } from './index.js';\nimport { REPORT_TYPES } from './types.js';\n\nconst require = createRequire(import.meta.url);\nconst { name, description, version } = require('../package.json');\n\nconst commander = new Command();\n\nconst transpile = async (code: string): Promise<string> => {\n const output = await transform(code, {\n filename: 'benchmark.ts',\n jsc: {\n parser: {\n syntax: 'typescript',\n tsx: false,\n dynamicImport: true,\n },\n target: 'esnext',\n },\n module: {\n type: 'es6',\n },\n });\n return output.code;\n};\n\ncommander\n .name(name)\n .description(description)\n .version(version)\n .argument('<path>', 'glob pattern to find benchmarks')\n .addOption(new Option('-r, --report-types [reportTypes...]', 'statistic types to include in the report').choices(REPORT_TYPES).default(DEFAULT_REPORT_TYPES))\n .addOption(new Option('-w, --workers [workers]', 'number of concurent workers').default(DEFAULT_WORKERS).argParser(parseInt))\n .addOption(new Option('-f, --format [format]', 'output format').default('simple').choices(['simple', 'json', 'pjson', 'table']))\n .addOption(new Option('--abs-threshold [absThreshold]', 'absolute error threshold in nanoseconds').argParser(parseInt))\n .addOption(new Option('--rel-threshold [relThreshold]', 'relative error threshold (fraction between 0 and 1)').argParser(parseInt))\n .addOption(new Option('--warmup-cycles [warmupCycles]', 'number of warmup cycles before measuring').argParser(parseInt))\n .addOption(new Option('--max-cycles [maxCycles]', 'maximum measurement cycles per feed').argParser(parseInt))\n .addOption(new Option('--min-cycles [minCycles]', 'minimum measurement cycles per feed').argParser(parseInt))\n .action(async (path, executeOptions) => {\n const files = await glob(path, { absolute: true, cwd: process.cwd() }).catch(() => []);\n for (const file of files) {\n const stats = await stat(file).catch(() => false as const);\n if (stats && stats.isFile()) {\n const content = await readFile(file, 'utf8');\n const code = await transpile(content);\n let instance: Benchmark<unknown> | undefined;\n const benchmark = (...args: Parameters<(typeof Benchmark)['create']>) => {\n if (instance) {\n throw new Error('Only one benchmark per file is supported');\n }\n instance = Benchmark.create(...args);\n return instance;\n };\n const script = new SourceTextModule(code, {\n context: createContext({\n benchmark,\n Buffer,\n }),\n });\n const imports = new Map();\n await script.link(async (specifier: string, referencingModule) => {\n if (imports.has(specifier)) {\n return imports.get(specifier);\n }\n const mod = await import(Module.isBuiltin(specifier) ? specifier : require.resolve(specifier));\n const exportNames = Object.keys(mod);\n const imported = new SyntheticModule(\n exportNames,\n () => {\n exportNames.forEach((key) => imported.setExport(key, mod[key]));\n },\n { identifier: specifier, context: referencingModule.context },\n );\n\n imports.set(specifier, imported);\n return imported;\n });\n await script.evaluate();\n\n if (instance) {\n const reports = await instance.execute(executeOptions);\n switch (executeOptions.format) {\n case 'json':\n {\n printJSONReports(reports);\n }\n break;\n case 'pjson':\n {\n printJSONReports(reports, 2);\n }\n break;\n case 'table':\n {\n printTableReports(reports);\n }\n break;\n default:\n printSimpleReports(reports);\n }\n }\n }\n }\n });\n\ncommander.parse(process.argv);\n"],"names":["createRequire","Module","SyntheticModule","createContext","SourceTextModule","stat","readFile","transform","Command","Option","glob","Benchmark","printTableReports","printJSONReports","printSimpleReports","DEFAULT_REPORT_TYPES","DEFAULT_WORKERS","REPORT_TYPES","require","url","name","description","version","commander","transpile","code","output","filename","jsc","parser","syntax","tsx","dynamicImport","target","module","type","argument","addOption","choices","default","argParser","parseInt","action","path","executeOptions","files","absolute","cwd","process","catch","file","stats","isFile","content","instance","benchmark","args","Error","create","script","context","Buffer","imports","Map","link","specifier","referencingModule","has","get","mod","isBuiltin","resolve","exportNames","Object","keys","imported","forEach","key","setExport","identifier","set","evaluate","reports","execute","format","parse","argv"],"mappings":"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"}
package/build/runner.cjs CHANGED
@@ -36,6 +36,8 @@ const benchmark = async ({ setup, teardown, pre, run: runRaw, post, data, warmup
36
36
  await pre?.(context, data);
37
37
  const result = runRaw(context, data);
38
38
  await post?.(context, data);
39
+ global.gc?.();
40
+ global.gc?.();
39
41
  const run = result instanceof Promise ? runAsync(runRaw) : runSync(runRaw);
40
42
  const start = Date.now();
41
43
  while(Date.now() - start < 1_000){
@@ -45,6 +47,8 @@ const benchmark = async ({ setup, teardown, pre, run: runRaw, post, data, warmup
45
47
  await pre?.(context, data);
46
48
  await run(context, data);
47
49
  await post?.(context, data);
50
+ global.gc?.();
51
+ global.gc?.();
48
52
  }
49
53
  let i = 0;
50
54
  let mean = 0n;
@@ -54,6 +58,8 @@ const benchmark = async ({ setup, teardown, pre, run: runRaw, post, data, warmup
54
58
  await pre?.(context, data);
55
59
  const duration = await run(context, data);
56
60
  await post?.(context, data);
61
+ global.gc?.();
62
+ global.gc?.();
57
63
  durations[i++] = duration;
58
64
  const delta = duration - mean;
59
65
  mean += delta / BigInt(i);
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/runner.ts"],"sourcesContent":["import { Options, Control } from './types.js';\n\nconst COMPLETE_VALUE = 100_00;\n\nconst runSync = (run: Function) => {\n return (...args: unknown[]) => {\n const start = process.hrtime.bigint();\n run(...args);\n return process.hrtime.bigint() - start;\n };\n};\n\nconst runAsync = (run: Function) => {\n return async (...args: unknown[]) => {\n const start = process.hrtime.bigint();\n await run(...args);\n return process.hrtime.bigint() - start;\n };\n};\n\nexport const benchmark = async <TContext, TInput>({\n setup,\n teardown,\n pre,\n run: runRaw,\n post,\n data,\n\n warmupCycles,\n minCycles,\n absThreshold,\n relThreshold,\n\n durationsSAB,\n controlSAB,\n}: Required<Options<TContext, TInput>>) => {\n const durations = new BigUint64Array(durationsSAB);\n const control = new Int32Array(controlSAB);\n\n control[Control.INDEX] = 0;\n control[Control.PROGRESS] = 0;\n control[Control.COMPLETE] = 255;\n\n const context = (await setup?.()) as TContext;\n const maxCycles = durations.length;\n\n try {\n await pre?.(context, data!);\n const result = runRaw(context, data!);\n await post?.(context, data!);\n const run = result instanceof Promise ? runAsync(runRaw) : runSync(runRaw);\n const start = Date.now();\n while (Date.now() - start < 1_000) {\n Math.sqrt(Math.random());\n }\n for (let i = 0; i < warmupCycles; i++) {\n await pre?.(context, data!);\n await run(context, data);\n await post?.(context, data!);\n }\n\n let i = 0;\n let mean = 0n;\n let m2 = 0n;\n\n while (true) {\n if (i >= maxCycles) break;\n\n await pre?.(context, data!);\n const duration = await run(context, data);\n await post?.(context, data!);\n\n durations[i++] = duration;\n const delta = duration - mean;\n mean += delta / BigInt(i);\n m2 += delta * (duration - mean);\n\n const progress = Math.max(i / maxCycles) * COMPLETE_VALUE;\n control[Control.PROGRESS] = progress;\n\n if (i >= minCycles) {\n const variance = Number(m2) / (i - 1);\n const stddev = Math.sqrt(variance);\n if (stddev <= Number(absThreshold)) {\n break;\n }\n\n const meanNum = Number(mean);\n const cov = stddev / (meanNum || 1);\n if (cov <= relThreshold) {\n break;\n }\n }\n }\n\n control[Control.INDEX] = i;\n control[Control.COMPLETE] = 0;\n } catch (e) {\n console.error(e && typeof e === 'object' && 'stack' in e ? e.stack : e);\n control[Control.COMPLETE] = 1;\n } finally {\n try {\n await teardown?.(context);\n } catch (e) {\n control[Control.COMPLETE] = 2;\n console.error(e && typeof e === 'object' && 'stack' in e ? e.stack : e);\n }\n }\n\n return control[Control.COMPLETE];\n};\n"],"names":["benchmark","COMPLETE_VALUE","runSync","run","args","start","process","hrtime","bigint","runAsync","setup","teardown","pre","runRaw","post","data","warmupCycles","minCycles","absThreshold","relThreshold","durationsSAB","controlSAB","durations","BigUint64Array","control","Int32Array","Control","INDEX","PROGRESS","COMPLETE","context","maxCycles","length","result","Promise","Date","now","Math","sqrt","random","i","mean","m2","duration","delta","BigInt","progress","max","variance","Number","stddev","meanNum","cov","e","console","error","stack"],"mappings":"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"}
1
+ {"version":3,"sources":["../src/runner.ts"],"sourcesContent":["import { Options, Control } from './types.js';\n\nconst COMPLETE_VALUE = 100_00;\n\nconst runSync = (run: Function) => {\n return (...args: unknown[]) => {\n const start = process.hrtime.bigint();\n run(...args);\n return process.hrtime.bigint() - start;\n };\n};\n\nconst runAsync = (run: Function) => {\n return async (...args: unknown[]) => {\n const start = process.hrtime.bigint();\n await run(...args);\n return process.hrtime.bigint() - start;\n };\n};\n\nexport const benchmark = async <TContext, TInput>({\n setup,\n teardown,\n pre,\n run: runRaw,\n post,\n data,\n\n warmupCycles,\n minCycles,\n absThreshold,\n relThreshold,\n\n durationsSAB,\n controlSAB,\n}: Required<Options<TContext, TInput>>) => {\n const durations = new BigUint64Array(durationsSAB);\n const control = new Int32Array(controlSAB);\n\n control[Control.INDEX] = 0;\n control[Control.PROGRESS] = 0;\n control[Control.COMPLETE] = 255;\n\n const context = (await setup?.()) as TContext;\n const maxCycles = durations.length;\n\n try {\n await pre?.(context, data!);\n const result = runRaw(context, data!);\n await post?.(context, data!);\n global.gc?.();\n global.gc?.();\n\n const run = result instanceof Promise ? runAsync(runRaw) : runSync(runRaw);\n const start = Date.now();\n while (Date.now() - start < 1_000) {\n Math.sqrt(Math.random());\n }\n for (let i = 0; i < warmupCycles; i++) {\n await pre?.(context, data!);\n await run(context, data);\n await post?.(context, data!);\n global.gc?.();\n global.gc?.();\n }\n\n let i = 0;\n let mean = 0n;\n let m2 = 0n;\n\n while (true) {\n if (i >= maxCycles) break;\n\n await pre?.(context, data!);\n const duration = await run(context, data);\n await post?.(context, data!);\n global.gc?.();\n global.gc?.();\n\n durations[i++] = duration;\n const delta = duration - mean;\n mean += delta / BigInt(i);\n m2 += delta * (duration - mean);\n\n const progress = Math.max(i / maxCycles) * COMPLETE_VALUE;\n control[Control.PROGRESS] = progress;\n\n if (i >= minCycles) {\n const variance = Number(m2) / (i - 1);\n const stddev = Math.sqrt(variance);\n if (stddev <= Number(absThreshold)) {\n break;\n }\n\n const meanNum = Number(mean);\n const cov = stddev / (meanNum || 1);\n if (cov <= relThreshold) {\n break;\n }\n }\n }\n\n control[Control.INDEX] = i;\n control[Control.COMPLETE] = 0;\n } catch (e) {\n console.error(e && typeof e === 'object' && 'stack' in e ? e.stack : e);\n control[Control.COMPLETE] = 1;\n } finally {\n try {\n await teardown?.(context);\n } catch (e) {\n control[Control.COMPLETE] = 2;\n console.error(e && typeof e === 'object' && 'stack' in e ? e.stack : e);\n }\n }\n\n return control[Control.COMPLETE];\n};\n"],"names":["benchmark","COMPLETE_VALUE","runSync","run","args","start","process","hrtime","bigint","runAsync","setup","teardown","pre","runRaw","post","data","warmupCycles","minCycles","absThreshold","relThreshold","durationsSAB","controlSAB","durations","BigUint64Array","control","Int32Array","Control","INDEX","PROGRESS","COMPLETE","context","maxCycles","length","result","global","gc","Promise","Date","now","Math","sqrt","random","i","mean","m2","duration","delta","BigInt","progress","max","variance","Number","stddev","meanNum","cov","e","console","error","stack"],"mappings":"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"}
package/build/runner.js CHANGED
@@ -26,6 +26,8 @@ export const benchmark = async ({ setup, teardown, pre, run: runRaw, post, data,
26
26
  await pre?.(context, data);
27
27
  const result = runRaw(context, data);
28
28
  await post?.(context, data);
29
+ global.gc?.();
30
+ global.gc?.();
29
31
  const run = result instanceof Promise ? runAsync(runRaw) : runSync(runRaw);
30
32
  const start = Date.now();
31
33
  while(Date.now() - start < 1_000){
@@ -35,6 +37,8 @@ export const benchmark = async ({ setup, teardown, pre, run: runRaw, post, data,
35
37
  await pre?.(context, data);
36
38
  await run(context, data);
37
39
  await post?.(context, data);
40
+ global.gc?.();
41
+ global.gc?.();
38
42
  }
39
43
  let i = 0;
40
44
  let mean = 0n;
@@ -44,6 +48,8 @@ export const benchmark = async ({ setup, teardown, pre, run: runRaw, post, data,
44
48
  await pre?.(context, data);
45
49
  const duration = await run(context, data);
46
50
  await post?.(context, data);
51
+ global.gc?.();
52
+ global.gc?.();
47
53
  durations[i++] = duration;
48
54
  const delta = duration - mean;
49
55
  mean += delta / BigInt(i);
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/runner.ts"],"sourcesContent":["import { Options, Control } from './types.js';\n\nconst COMPLETE_VALUE = 100_00;\n\nconst runSync = (run: Function) => {\n return (...args: unknown[]) => {\n const start = process.hrtime.bigint();\n run(...args);\n return process.hrtime.bigint() - start;\n };\n};\n\nconst runAsync = (run: Function) => {\n return async (...args: unknown[]) => {\n const start = process.hrtime.bigint();\n await run(...args);\n return process.hrtime.bigint() - start;\n };\n};\n\nexport const benchmark = async <TContext, TInput>({\n setup,\n teardown,\n pre,\n run: runRaw,\n post,\n data,\n\n warmupCycles,\n minCycles,\n absThreshold,\n relThreshold,\n\n durationsSAB,\n controlSAB,\n}: Required<Options<TContext, TInput>>) => {\n const durations = new BigUint64Array(durationsSAB);\n const control = new Int32Array(controlSAB);\n\n control[Control.INDEX] = 0;\n control[Control.PROGRESS] = 0;\n control[Control.COMPLETE] = 255;\n\n const context = (await setup?.()) as TContext;\n const maxCycles = durations.length;\n\n try {\n await pre?.(context, data!);\n const result = runRaw(context, data!);\n await post?.(context, data!);\n const run = result instanceof Promise ? runAsync(runRaw) : runSync(runRaw);\n const start = Date.now();\n while (Date.now() - start < 1_000) {\n Math.sqrt(Math.random());\n }\n for (let i = 0; i < warmupCycles; i++) {\n await pre?.(context, data!);\n await run(context, data);\n await post?.(context, data!);\n }\n\n let i = 0;\n let mean = 0n;\n let m2 = 0n;\n\n while (true) {\n if (i >= maxCycles) break;\n\n await pre?.(context, data!);\n const duration = await run(context, data);\n await post?.(context, data!);\n\n durations[i++] = duration;\n const delta = duration - mean;\n mean += delta / BigInt(i);\n m2 += delta * (duration - mean);\n\n const progress = Math.max(i / maxCycles) * COMPLETE_VALUE;\n control[Control.PROGRESS] = progress;\n\n if (i >= minCycles) {\n const variance = Number(m2) / (i - 1);\n const stddev = Math.sqrt(variance);\n if (stddev <= Number(absThreshold)) {\n break;\n }\n\n const meanNum = Number(mean);\n const cov = stddev / (meanNum || 1);\n if (cov <= relThreshold) {\n break;\n }\n }\n }\n\n control[Control.INDEX] = i;\n control[Control.COMPLETE] = 0;\n } catch (e) {\n console.error(e && typeof e === 'object' && 'stack' in e ? e.stack : e);\n control[Control.COMPLETE] = 1;\n } finally {\n try {\n await teardown?.(context);\n } catch (e) {\n control[Control.COMPLETE] = 2;\n console.error(e && typeof e === 'object' && 'stack' in e ? e.stack : e);\n }\n }\n\n return control[Control.COMPLETE];\n};\n"],"names":["Control","COMPLETE_VALUE","runSync","run","args","start","process","hrtime","bigint","runAsync","benchmark","setup","teardown","pre","runRaw","post","data","warmupCycles","minCycles","absThreshold","relThreshold","durationsSAB","controlSAB","durations","BigUint64Array","control","Int32Array","INDEX","PROGRESS","COMPLETE","context","maxCycles","length","result","Promise","Date","now","Math","sqrt","random","i","mean","m2","duration","delta","BigInt","progress","max","variance","Number","stddev","meanNum","cov","e","console","error","stack"],"mappings":"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"}
1
+ {"version":3,"sources":["../src/runner.ts"],"sourcesContent":["import { Options, Control } from './types.js';\n\nconst COMPLETE_VALUE = 100_00;\n\nconst runSync = (run: Function) => {\n return (...args: unknown[]) => {\n const start = process.hrtime.bigint();\n run(...args);\n return process.hrtime.bigint() - start;\n };\n};\n\nconst runAsync = (run: Function) => {\n return async (...args: unknown[]) => {\n const start = process.hrtime.bigint();\n await run(...args);\n return process.hrtime.bigint() - start;\n };\n};\n\nexport const benchmark = async <TContext, TInput>({\n setup,\n teardown,\n pre,\n run: runRaw,\n post,\n data,\n\n warmupCycles,\n minCycles,\n absThreshold,\n relThreshold,\n\n durationsSAB,\n controlSAB,\n}: Required<Options<TContext, TInput>>) => {\n const durations = new BigUint64Array(durationsSAB);\n const control = new Int32Array(controlSAB);\n\n control[Control.INDEX] = 0;\n control[Control.PROGRESS] = 0;\n control[Control.COMPLETE] = 255;\n\n const context = (await setup?.()) as TContext;\n const maxCycles = durations.length;\n\n try {\n await pre?.(context, data!);\n const result = runRaw(context, data!);\n await post?.(context, data!);\n global.gc?.();\n global.gc?.();\n\n const run = result instanceof Promise ? runAsync(runRaw) : runSync(runRaw);\n const start = Date.now();\n while (Date.now() - start < 1_000) {\n Math.sqrt(Math.random());\n }\n for (let i = 0; i < warmupCycles; i++) {\n await pre?.(context, data!);\n await run(context, data);\n await post?.(context, data!);\n global.gc?.();\n global.gc?.();\n }\n\n let i = 0;\n let mean = 0n;\n let m2 = 0n;\n\n while (true) {\n if (i >= maxCycles) break;\n\n await pre?.(context, data!);\n const duration = await run(context, data);\n await post?.(context, data!);\n global.gc?.();\n global.gc?.();\n\n durations[i++] = duration;\n const delta = duration - mean;\n mean += delta / BigInt(i);\n m2 += delta * (duration - mean);\n\n const progress = Math.max(i / maxCycles) * COMPLETE_VALUE;\n control[Control.PROGRESS] = progress;\n\n if (i >= minCycles) {\n const variance = Number(m2) / (i - 1);\n const stddev = Math.sqrt(variance);\n if (stddev <= Number(absThreshold)) {\n break;\n }\n\n const meanNum = Number(mean);\n const cov = stddev / (meanNum || 1);\n if (cov <= relThreshold) {\n break;\n }\n }\n }\n\n control[Control.INDEX] = i;\n control[Control.COMPLETE] = 0;\n } catch (e) {\n console.error(e && typeof e === 'object' && 'stack' in e ? e.stack : e);\n control[Control.COMPLETE] = 1;\n } finally {\n try {\n await teardown?.(context);\n } catch (e) {\n control[Control.COMPLETE] = 2;\n console.error(e && typeof e === 'object' && 'stack' in e ? e.stack : e);\n }\n }\n\n return control[Control.COMPLETE];\n};\n"],"names":["Control","COMPLETE_VALUE","runSync","run","args","start","process","hrtime","bigint","runAsync","benchmark","setup","teardown","pre","runRaw","post","data","warmupCycles","minCycles","absThreshold","relThreshold","durationsSAB","controlSAB","durations","BigUint64Array","control","Int32Array","INDEX","PROGRESS","COMPLETE","context","maxCycles","length","result","global","gc","Promise","Date","now","Math","sqrt","random","i","mean","m2","duration","delta","BigInt","progress","max","variance","Number","stddev","meanNum","cov","e","console","error","stack"],"mappings":"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"}
package/build/worker.cjs CHANGED
@@ -2,6 +2,12 @@
2
2
  Object.defineProperty(exports, "__esModule", {
3
3
  value: true
4
4
  });
5
+ Object.defineProperty(exports, "exitCode", {
6
+ enumerable: true,
7
+ get: function() {
8
+ return exitCode;
9
+ }
10
+ });
5
11
  const _nodeworker_threads = require("node:worker_threads");
6
12
  const _runnercjs = require("./runner.cjs");
7
13
  const { setupCode, teardownCode, preCode, runCode, postCode, data, warmupCycles, minCycles, absThreshold, relThreshold, durationsSAB, controlSAB } = _nodeworker_threads.workerData;
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/worker.ts"],"sourcesContent":["import { workerData } from 'node:worker_threads';\nimport { benchmark } from './runner.js';\nimport { SetupFn, TeardownFn, StepFn, WorkerOptions } from './types.js';\n\nconst {\n setupCode,\n teardownCode,\n preCode,\n runCode,\n postCode,\n data,\n\n warmupCycles,\n minCycles,\n absThreshold,\n relThreshold,\n\n durationsSAB,\n controlSAB,\n}: WorkerOptions = workerData;\n\nconst setup: SetupFn<unknown> = setupCode && Function(`return ${setupCode};`)();\nconst teardown: TeardownFn<unknown> = teardownCode && Function(`return ${teardownCode};`)();\n\nconst pre: StepFn<unknown, unknown> = preCode && Function(`return ${preCode};`)();\nconst run: StepFn<unknown, unknown> = runCode && Function(`return ${runCode};`)();\nconst post: StepFn<unknown, unknown> = postCode && Function(`return ${postCode};`)();\n\nconst exitCode = await benchmark({\n setup,\n teardown,\n pre,\n run,\n post,\n data,\n\n warmupCycles,\n minCycles,\n absThreshold,\n relThreshold,\n\n durationsSAB,\n controlSAB,\n});\n\nprocess.exit(exitCode);\n"],"names":["setupCode","teardownCode","preCode","runCode","postCode","data","warmupCycles","minCycles","absThreshold","relThreshold","durationsSAB","controlSAB","workerData","setup","Function","teardown","pre","run","post","exitCode","benchmark","process","exit"],"mappings":";;;;oCAA2B;2BACD;AAG1B,MAAM,EACJA,SAAS,EACTC,YAAY,EACZC,OAAO,EACPC,OAAO,EACPC,QAAQ,EACRC,IAAI,EAEJC,YAAY,EACZC,SAAS,EACTC,YAAY,EACZC,YAAY,EAEZC,YAAY,EACZC,UAAU,EACX,GAAkBC,8BAAU;AAE7B,MAAMC,QAA0Bb,aAAac,SAAS,CAAC,OAAO,EAAEd,UAAU,CAAC,CAAC;AAC5E,MAAMe,WAAgCd,gBAAgBa,SAAS,CAAC,OAAO,EAAEb,aAAa,CAAC,CAAC;AAExF,MAAMe,MAAgCd,WAAWY,SAAS,CAAC,OAAO,EAAEZ,QAAQ,CAAC,CAAC;AAC9E,MAAMe,MAAgCd,WAAWW,SAAS,CAAC,OAAO,EAAEX,QAAQ,CAAC,CAAC;AAC9E,MAAMe,OAAiCd,YAAYU,SAAS,CAAC,OAAO,EAAEV,SAAS,CAAC,CAAC;AAEjF,MAAMe,WAAW,MAAMC,IAAAA,oBAAS,EAAC;IAC/BP;IACAE;IACAC;IACAC;IACAC;IACAb;IAEAC;IACAC;IACAC;IACAC;IAEAC;IACAC;AACF;AAEAU,QAAQC,IAAI,CAACH"}
1
+ {"version":3,"sources":["../src/worker.ts"],"sourcesContent":["import { workerData } from 'node:worker_threads';\nimport { benchmark } from './runner.js';\nimport { SetupFn, TeardownFn, StepFn, WorkerOptions } from './types.js';\n\nconst {\n setupCode,\n teardownCode,\n preCode,\n runCode,\n postCode,\n data,\n\n warmupCycles,\n minCycles,\n absThreshold,\n relThreshold,\n\n durationsSAB,\n controlSAB,\n}: WorkerOptions = workerData;\n\nconst setup: SetupFn<unknown> = setupCode && Function(`return ${setupCode};`)();\nconst teardown: TeardownFn<unknown> = teardownCode && Function(`return ${teardownCode};`)();\n\nconst pre: StepFn<unknown, unknown> = preCode && Function(`return ${preCode};`)();\nconst run: StepFn<unknown, unknown> = runCode && Function(`return ${runCode};`)();\nconst post: StepFn<unknown, unknown> = postCode && Function(`return ${postCode};`)();\n\nexport const exitCode = await benchmark({\n setup,\n teardown,\n pre,\n run,\n post,\n data,\n\n warmupCycles,\n minCycles,\n absThreshold,\n relThreshold,\n\n durationsSAB,\n controlSAB,\n});\n\nprocess.exit(exitCode);\n"],"names":["exitCode","setupCode","teardownCode","preCode","runCode","postCode","data","warmupCycles","minCycles","absThreshold","relThreshold","durationsSAB","controlSAB","workerData","setup","Function","teardown","pre","run","post","benchmark","process","exit"],"mappings":";;;;+BA4BaA;;;eAAAA;;;oCA5Bc;2BACD;AAG1B,MAAM,EACJC,SAAS,EACTC,YAAY,EACZC,OAAO,EACPC,OAAO,EACPC,QAAQ,EACRC,IAAI,EAEJC,YAAY,EACZC,SAAS,EACTC,YAAY,EACZC,YAAY,EAEZC,YAAY,EACZC,UAAU,EACX,GAAkBC,8BAAU;AAE7B,MAAMC,QAA0Bb,aAAac,SAAS,CAAC,OAAO,EAAEd,UAAU,CAAC,CAAC;AAC5E,MAAMe,WAAgCd,gBAAgBa,SAAS,CAAC,OAAO,EAAEb,aAAa,CAAC,CAAC;AAExF,MAAMe,MAAgCd,WAAWY,SAAS,CAAC,OAAO,EAAEZ,QAAQ,CAAC,CAAC;AAC9E,MAAMe,MAAgCd,WAAWW,SAAS,CAAC,OAAO,EAAEX,QAAQ,CAAC,CAAC;AAC9E,MAAMe,OAAiCd,YAAYU,SAAS,CAAC,OAAO,EAAEV,SAAS,CAAC,CAAC;AAE1E,MAAML,WAAW,MAAMoB,IAAAA,oBAAS,EAAC;IACtCN;IACAE;IACAC;IACAC;IACAC;IACAb;IAEAC;IACAC;IACAC;IACAC;IAEAC;IACAC;AACF;AAEAS,QAAQC,IAAI,CAACtB"}
package/build/worker.d.ts CHANGED
@@ -1 +1 @@
1
- export {};
1
+ export declare const exitCode: number;
package/build/worker.js CHANGED
@@ -6,7 +6,7 @@ const teardown = teardownCode && Function(`return ${teardownCode};`)();
6
6
  const pre = preCode && Function(`return ${preCode};`)();
7
7
  const run = runCode && Function(`return ${runCode};`)();
8
8
  const post = postCode && Function(`return ${postCode};`)();
9
- const exitCode = await benchmark({
9
+ export const exitCode = await benchmark({
10
10
  setup,
11
11
  teardown,
12
12
  pre,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/worker.ts"],"sourcesContent":["import { workerData } from 'node:worker_threads';\nimport { benchmark } from './runner.js';\nimport { SetupFn, TeardownFn, StepFn, WorkerOptions } from './types.js';\n\nconst {\n setupCode,\n teardownCode,\n preCode,\n runCode,\n postCode,\n data,\n\n warmupCycles,\n minCycles,\n absThreshold,\n relThreshold,\n\n durationsSAB,\n controlSAB,\n}: WorkerOptions = workerData;\n\nconst setup: SetupFn<unknown> = setupCode && Function(`return ${setupCode};`)();\nconst teardown: TeardownFn<unknown> = teardownCode && Function(`return ${teardownCode};`)();\n\nconst pre: StepFn<unknown, unknown> = preCode && Function(`return ${preCode};`)();\nconst run: StepFn<unknown, unknown> = runCode && Function(`return ${runCode};`)();\nconst post: StepFn<unknown, unknown> = postCode && Function(`return ${postCode};`)();\n\nconst exitCode = await benchmark({\n setup,\n teardown,\n pre,\n run,\n post,\n data,\n\n warmupCycles,\n minCycles,\n absThreshold,\n relThreshold,\n\n durationsSAB,\n controlSAB,\n});\n\nprocess.exit(exitCode);\n"],"names":["workerData","benchmark","setupCode","teardownCode","preCode","runCode","postCode","data","warmupCycles","minCycles","absThreshold","relThreshold","durationsSAB","controlSAB","setup","Function","teardown","pre","run","post","exitCode","process","exit"],"mappings":"AAAA,SAASA,UAAU,QAAQ,sBAAsB;AACjD,SAASC,SAAS,QAAQ,cAAc;AAGxC,MAAM,EACJC,SAAS,EACTC,YAAY,EACZC,OAAO,EACPC,OAAO,EACPC,QAAQ,EACRC,IAAI,EAEJC,YAAY,EACZC,SAAS,EACTC,YAAY,EACZC,YAAY,EAEZC,YAAY,EACZC,UAAU,EACX,GAAkBb;AAEnB,MAAMc,QAA0BZ,aAAaa,SAAS,CAAC,OAAO,EAAEb,UAAU,CAAC,CAAC;AAC5E,MAAMc,WAAgCb,gBAAgBY,SAAS,CAAC,OAAO,EAAEZ,aAAa,CAAC,CAAC;AAExF,MAAMc,MAAgCb,WAAWW,SAAS,CAAC,OAAO,EAAEX,QAAQ,CAAC,CAAC;AAC9E,MAAMc,MAAgCb,WAAWU,SAAS,CAAC,OAAO,EAAEV,QAAQ,CAAC,CAAC;AAC9E,MAAMc,OAAiCb,YAAYS,SAAS,CAAC,OAAO,EAAET,SAAS,CAAC,CAAC;AAEjF,MAAMc,WAAW,MAAMnB,UAAU;IAC/Ba;IACAE;IACAC;IACAC;IACAC;IACAZ;IAEAC;IACAC;IACAC;IACAC;IAEAC;IACAC;AACF;AAEAQ,QAAQC,IAAI,CAACF"}
1
+ {"version":3,"sources":["../src/worker.ts"],"sourcesContent":["import { workerData } from 'node:worker_threads';\nimport { benchmark } from './runner.js';\nimport { SetupFn, TeardownFn, StepFn, WorkerOptions } from './types.js';\n\nconst {\n setupCode,\n teardownCode,\n preCode,\n runCode,\n postCode,\n data,\n\n warmupCycles,\n minCycles,\n absThreshold,\n relThreshold,\n\n durationsSAB,\n controlSAB,\n}: WorkerOptions = workerData;\n\nconst setup: SetupFn<unknown> = setupCode && Function(`return ${setupCode};`)();\nconst teardown: TeardownFn<unknown> = teardownCode && Function(`return ${teardownCode};`)();\n\nconst pre: StepFn<unknown, unknown> = preCode && Function(`return ${preCode};`)();\nconst run: StepFn<unknown, unknown> = runCode && Function(`return ${runCode};`)();\nconst post: StepFn<unknown, unknown> = postCode && Function(`return ${postCode};`)();\n\nexport const exitCode = await benchmark({\n setup,\n teardown,\n pre,\n run,\n post,\n data,\n\n warmupCycles,\n minCycles,\n absThreshold,\n relThreshold,\n\n durationsSAB,\n controlSAB,\n});\n\nprocess.exit(exitCode);\n"],"names":["workerData","benchmark","setupCode","teardownCode","preCode","runCode","postCode","data","warmupCycles","minCycles","absThreshold","relThreshold","durationsSAB","controlSAB","setup","Function","teardown","pre","run","post","exitCode","process","exit"],"mappings":"AAAA,SAASA,UAAU,QAAQ,sBAAsB;AACjD,SAASC,SAAS,QAAQ,cAAc;AAGxC,MAAM,EACJC,SAAS,EACTC,YAAY,EACZC,OAAO,EACPC,OAAO,EACPC,QAAQ,EACRC,IAAI,EAEJC,YAAY,EACZC,SAAS,EACTC,YAAY,EACZC,YAAY,EAEZC,YAAY,EACZC,UAAU,EACX,GAAkBb;AAEnB,MAAMc,QAA0BZ,aAAaa,SAAS,CAAC,OAAO,EAAEb,UAAU,CAAC,CAAC;AAC5E,MAAMc,WAAgCb,gBAAgBY,SAAS,CAAC,OAAO,EAAEZ,aAAa,CAAC,CAAC;AAExF,MAAMc,MAAgCb,WAAWW,SAAS,CAAC,OAAO,EAAEX,QAAQ,CAAC,CAAC;AAC9E,MAAMc,MAAgCb,WAAWU,SAAS,CAAC,OAAO,EAAEV,QAAQ,CAAC,CAAC;AAC9E,MAAMc,OAAiCb,YAAYS,SAAS,CAAC,OAAO,EAAET,SAAS,CAAC,CAAC;AAEjF,OAAO,MAAMc,WAAW,MAAMnB,UAAU;IACtCa;IACAE;IACAC;IACAC;IACAC;IACAZ;IAEAC;IACAC;IACAC;IACAC;IAEAC;IACAC;AACF,GAAG;AAEHQ,QAAQC,IAAI,CAACF"}
@@ -29,7 +29,7 @@ v8Target
29
29
  // executed before measurement
30
30
  })
31
31
  .post(async (_ctx, _input) => {
32
- // executed before measurement
32
+ // executed after measurement
33
33
  });
34
34
 
35
35
  v8Target
@@ -64,11 +64,11 @@ jsonTarget
64
64
  // executed before measurement
65
65
  })
66
66
  .post(async (_ctx, _input) => {
67
- // executed before measurement
67
+ // executed after measurement
68
68
  });
69
69
 
70
70
  jsonTarget
71
- .measure('parsse', ({ parse, serialized }) => {
71
+ .measure('parse', ({ parse, serialized }) => {
72
72
  parse(serialized);
73
73
  })
74
74
  .pre(async (ctx, input) => {
@@ -0,0 +1,22 @@
1
+ // Demonstrates custom report types and statistics
2
+ // CLI mode: npx overtake examples/custom-reports.ts -f table -r ops mean median p95 p99
3
+ // Programmatic mode: node examples/custom-reports.js
4
+
5
+ import { Benchmark, printTableReports } from '../build/index.js';
6
+
7
+ const performanceSuite = new Benchmark('10K numbers', () => Array.from({ length: 10_000 }, () => Math.random() * 1000));
8
+
9
+ // Compare different rounding methods
10
+ performanceSuite.target('rounding methods').measure('Math.floor', (_, numbers) => {
11
+ for (const n of numbers) Math.floor(n);
12
+ });
13
+
14
+ // Execute with custom statistics
15
+ const reports = await performanceSuite.execute({
16
+ workers: 4,
17
+ minCycles: 100,
18
+ maxCycles: 500,
19
+ reportTypes: ['ops', 'mean', 'median', 'p50', 'p95', 'p99', 'min', 'max'] as const,
20
+ });
21
+
22
+ printTableReports(reports);
@@ -0,0 +1,51 @@
1
+ // Demonstrates correct import patterns for worker context
2
+ // Run: npx overtake examples/imports.ts
3
+
4
+ const importExamples = benchmark('test data', () => Buffer.from('hello world'));
5
+
6
+ // Node built-in modules work normally
7
+ importExamples
8
+ .target('node built-ins', async () => {
9
+ const { createHash } = await import('node:crypto');
10
+ return { createHash };
11
+ })
12
+ .measure('sha256 hash', ({ createHash }, buffer) => {
13
+ createHash('sha256').update(buffer).digest('hex');
14
+ });
15
+
16
+ // IMPORTANT: Local files need absolute paths
17
+ // Relative imports resolve from node_modules/overtake/build/, not your project
18
+ const localFilesTarget = importExamples.target('local files', async () => {
19
+ const { join } = await import('node:path');
20
+
21
+ // Build absolute path to your local module
22
+ const localModulePath = join(process.cwd(), './build/types.js');
23
+ const { DEFAULT_CYCLES, Control } = await import(localModulePath);
24
+
25
+ return {
26
+ DEFAULT_CYCLES,
27
+ Control,
28
+ // You can also define functions here
29
+ processData: (data: Buffer) => data.length,
30
+ };
31
+ });
32
+
33
+ localFilesTarget.measure('use imported constant', ({ DEFAULT_CYCLES }) => {
34
+ DEFAULT_CYCLES > 100;
35
+ });
36
+
37
+ localFilesTarget.measure('use local function', ({ processData }, buffer) => {
38
+ processData(buffer);
39
+ });
40
+
41
+ // Memory management pattern - prevent GC during measurements
42
+ importExamples
43
+ .target('memory management', () => {
44
+ const gcBlock = new Set();
45
+ return { gcBlock };
46
+ })
47
+ .measure('without GC', ({ gcBlock }, buffer) => {
48
+ const result = Buffer.concat([buffer, buffer]);
49
+ gcBlock.add(result); // Keep reference alive
50
+ result.length;
51
+ });
@@ -1,16 +1,15 @@
1
- // run using the following command
2
- // npx overtake examples/quick-start.ts
1
+ // Minimal example - comparing array sum algorithms
2
+ // Run: npx overtake examples/quick-start.ts
3
3
 
4
- const sumSuite = benchmark('1M array', () => Array.from({ length: 1_000_000 }, (_, idx) => idx));
4
+ const sumBenchmark = benchmark('1M numbers', () => Array.from({ length: 1_000_000 }, (_, index) => index));
5
5
 
6
- sumSuite.target('for loop').measure('sum', (_, input) => {
7
- const n = input.length;
6
+ sumBenchmark.target('for loop').measure('sum', (_, numbers) => {
8
7
  let sum = 0;
9
- for (let i = 0; i < n; i++) {
10
- sum += input[i];
8
+ for (let i = 0; i < numbers.length; i++) {
9
+ sum += numbers[i];
11
10
  }
12
11
  });
13
12
 
14
- sumSuite.target('reduce').measure('sum', (_, input) => {
15
- input.reduce((a, b) => a + b, 0);
13
+ sumBenchmark.target('reduce').measure('sum', (_, numbers) => {
14
+ numbers.reduce((accumulator, current) => accumulator + current, 0);
16
15
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "overtake",
3
- "version": "1.0.4",
3
+ "version": "1.0.5",
4
4
  "description": "NodeJS performance benchmark",
5
5
  "type": "module",
6
6
  "types": "build/index.d.ts",
package/src/cli.ts CHANGED
@@ -59,7 +59,10 @@ commander
59
59
  return instance;
60
60
  };
61
61
  const script = new SourceTextModule(code, {
62
- context: createContext({ benchmark }),
62
+ context: createContext({
63
+ benchmark,
64
+ Buffer,
65
+ }),
63
66
  });
64
67
  const imports = new Map();
65
68
  await script.link(async (specifier: string, referencingModule) => {
package/src/runner.ts CHANGED
@@ -48,6 +48,9 @@ export const benchmark = async <TContext, TInput>({
48
48
  await pre?.(context, data!);
49
49
  const result = runRaw(context, data!);
50
50
  await post?.(context, data!);
51
+ global.gc?.();
52
+ global.gc?.();
53
+
51
54
  const run = result instanceof Promise ? runAsync(runRaw) : runSync(runRaw);
52
55
  const start = Date.now();
53
56
  while (Date.now() - start < 1_000) {
@@ -57,6 +60,8 @@ export const benchmark = async <TContext, TInput>({
57
60
  await pre?.(context, data!);
58
61
  await run(context, data);
59
62
  await post?.(context, data!);
63
+ global.gc?.();
64
+ global.gc?.();
60
65
  }
61
66
 
62
67
  let i = 0;
@@ -69,6 +74,8 @@ export const benchmark = async <TContext, TInput>({
69
74
  await pre?.(context, data!);
70
75
  const duration = await run(context, data);
71
76
  await post?.(context, data!);
77
+ global.gc?.();
78
+ global.gc?.();
72
79
 
73
80
  durations[i++] = duration;
74
81
  const delta = duration - mean;
package/src/worker.ts CHANGED
@@ -26,7 +26,7 @@ const pre: StepFn<unknown, unknown> = preCode && Function(`return ${preCode};`)(
26
26
  const run: StepFn<unknown, unknown> = runCode && Function(`return ${runCode};`)();
27
27
  const post: StepFn<unknown, unknown> = postCode && Function(`return ${postCode};`)();
28
28
 
29
- const exitCode = await benchmark({
29
+ export const exitCode = await benchmark({
30
30
  setup,
31
31
  teardown,
32
32
  pre,
package/CLAUDE.md DELETED
@@ -1,145 +0,0 @@
1
- # CLAUDE.md
2
-
3
- This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
-
5
- ## Project Overview
6
-
7
- Overtake is a high-precision JavaScript/TypeScript benchmarking library that uses worker thread isolation and statistical convergence to provide accurate performance measurements. It solves common benchmarking problems like JIT optimization interference and cross-benchmark contamination.
8
-
9
- ## Development Commands
10
-
11
- ```bash
12
- # Build the project (uses inop for transpilation + tsc for declarations only)
13
- npm run build
14
- # or with pnpm
15
- pnpm build
16
-
17
- # Run tests (uses Jest with SWC transpilation - no test files currently exist)
18
- npm test
19
-
20
- # Execute benchmarks via CLI
21
- npx overtake "examples/*.ts" -f table -r ops mean p95
22
-
23
- # Run a single benchmark file
24
- npx overtake examples/quick-start.ts
25
-
26
- # Start the CLI directly (requires argument)
27
- npm start examples/quick-start.ts
28
-
29
- # Format code with Prettier (via pre-commit hook)
30
- npx prettier --write "src/**/*.ts" "examples/**/*.ts"
31
-
32
- # Note: Package manager is pnpm@10.14.0 (see packageManager field in package.json)
33
- ```
34
-
35
- ## Architecture
36
-
37
- ### Core Components
38
-
39
- 1. **Benchmark Class** (`src/index.ts`): Main API with fluent interface for defining benchmarks using feed → target → measure pattern
40
- 2. **Executor** (`src/executor.ts`): Worker thread pool management using async queues and SharedArrayBuffer for zero-copy communication
41
- 3. **Worker** (`src/worker.ts`): Isolated execution environment with fresh V8 context per benchmark
42
- 4. **Runner** (`src/runner.ts`): Handles warmup cycles, statistical convergence detection, and timing collection
43
- 5. **Reporter** (`src/reporter.ts`): Statistical analysis and result formatting (ops/sec, percentiles, mean/median/mode)
44
-
45
- ### Key Design Patterns
46
-
47
- - **Worker Thread Isolation**: Each benchmark runs in separate thread to prevent contamination
48
- - **SharedArrayBuffer Communication**: Zero-copy data transfer for high-precision bigint timing
49
- - **Statistical Convergence**: Automatic cycle adjustment based on configurable confidence thresholds
50
- - **Dynamic Code Execution**: Function serialization across threads with VM module sandboxing
51
-
52
- ### API Structure
53
-
54
- ```typescript
55
- // Fluent API pattern
56
- benchmark('name', feedFunction)
57
- .target('implementation')
58
- .measure('operation', measureFunction)
59
- .execute() // Returns Promise<Report[]>
60
- ```
61
-
62
- ## Technical Requirements
63
-
64
- - Node.js >=22 (uses modern features like VM modules)
65
- - ES modules only (no CommonJS)
66
- - TypeScript with ESNext target
67
- - Uses SWC for transpilation (not tsc for builds)
68
-
69
- ## Important Implementation Notes
70
-
71
- - **CRITICAL**: All imports must be dynamic inside target callbacks since they run in worker threads:
72
- ```typescript
73
- // CORRECT - dynamic import inside target
74
- .target('V8', async () => {
75
- const { serialize } = await import('node:v8');
76
- return { serialize };
77
- })
78
-
79
- // WRONG - static import at top level
80
- import { serialize } from 'node:v8';
81
- .target('V8', () => ({ serialize }))
82
- ```
83
- - Timing uses `process.hrtime.bigint()` for nanosecond precision
84
- - Worker threads communicate via SharedArrayBuffer to minimize overhead
85
- - Build process uses `inop` tool for transpilation followed by tsc for declarations only
86
- - Test files should match `*.spec.ts` or `*.test.ts` patterns (currently no tests exist)
87
-
88
- ## API Usage Modes
89
-
90
- ### CLI Mode (Global `benchmark` function)
91
- - Used when running via `npx overtake file.ts`
92
- - CLI provides global `benchmark` function automatically
93
- - No imports needed, no `.execute()` call required
94
- - Results printed based on CLI flags
95
-
96
- Example (examples/quick-start.ts):
97
- ```typescript
98
- const suite = benchmark('name', () => data);
99
- suite.target('impl').measure('op', (ctx, input) => { /* ... */ });
100
- ```
101
-
102
- ### Programmatic Mode (Import Benchmark class)
103
- - Used for standalone scripts with custom execution
104
- - Must import Benchmark class and printer functions
105
- - Must call `.execute()` and handle results
106
-
107
- Example (examples/complete.ts):
108
- ```typescript
109
- import { Benchmark, printJSONReports } from '../build/index.js';
110
- const suite = new Benchmark('name', () => data);
111
- // ... define targets and measures
112
- const reports = await suite.execute({ /* options */ });
113
- printJSONReports(reports, 2);
114
- ```
115
-
116
- ## Common Issues and Solutions
117
-
118
- ### TypeScript Transpilation
119
- - CLI uses SWC's `transform` function to strip TypeScript types
120
- - If you see "Missing initializer in const declaration", it means TypeScript wasn't transpiled
121
- - The transpiler in `src/cli.ts` must use `transform`, not `parse`/`print`
122
-
123
- ### Benchmarks Not Running
124
- - CLI mode: Ensure using global `benchmark` function (no import)
125
- - Programmatic mode: Must call `.execute()` and handle results
126
- - Check that worker count doesn't exceed CPU cores
127
-
128
- ### Memory Management Patterns
129
- - Use `gcBlock` Set to prevent garbage collection during measurements:
130
- ```typescript
131
- .target('name', () => {
132
- const gcBlock = new Set(); // Prevents GC
133
- return { gcBlock };
134
- })
135
- .measure('op', ({ gcBlock }, input) => {
136
- gcBlock.add(result); // Keep reference alive
137
- })
138
- ```
139
-
140
- ## Code Quality Tools
141
-
142
- - **Formatter**: Prettier (config in `.prettierrc`) - runs automatically on commit via husky pre-commit hook
143
- - **Test Runner**: Jest with SWC transpilation (config in `jest.config.js`)
144
- - **TypeScript Config**: Strict mode enabled, targets ESNext with NodeNext modules
145
- - **No linter or type-check commands**: Add these if needed in the future
@@ -1,17 +0,0 @@
1
- const copySuite = benchmark('1M array of strings', () => Array.from({ length: 1_000_000 }, (_, idx) => `${idx}`))
2
- .feed('1M array of numbers', () => Array.from({ length: 1_000_000 }, (_, idx) => idx))
3
- .feed('1M typed array', () => new Uint32Array(1_000_000).map((_, idx) => idx));
4
-
5
- copySuite.target('for loop').measure('copy half', (_, input) => {
6
- const n = input?.length ?? 0;
7
- const mid = n / 2;
8
- for (let i = 0; i < mid; i++) {
9
- input[i + mid] = input[i];
10
- }
11
- });
12
-
13
- copySuite.target('copyWithin').measure('copy half', (_, input) => {
14
- const n = input?.length ?? 0;
15
- const mid = n / 2;
16
- input.copyWithin(mid, 0, mid);
17
- });
@@ -1,41 +0,0 @@
1
- import { Benchmark, printSimpleReports } from '../build/index.js';
2
-
3
- const objectMergeSuite = new Benchmark('1K array of objects', () => Array.from({ length: 1_000 }, (_, idx) => ({ [idx]: idx })));
4
-
5
- objectMergeSuite.target('reduce destructure').measure('data', (_, input) => {
6
- input.reduce((acc, obj) => {
7
- return { ...acc, ...obj };
8
- }, {});
9
- });
10
-
11
- objectMergeSuite.target('reduce assign').measure('data', (_, input) => {
12
- input.reduce((acc, obj) => {
13
- Object.assign(acc, obj);
14
- return acc;
15
- }, {});
16
- });
17
-
18
- objectMergeSuite.target('forEach assign').measure('data', (_, input) => {
19
- const result = {};
20
- input.forEach((obj) => {
21
- Object.assign(result, obj);
22
- });
23
- });
24
-
25
- objectMergeSuite.target('for assign').measure('data', (_, input) => {
26
- const result = {};
27
- for (let i = 0; i < input.length; i++) {
28
- Object.assign(result, input[i]);
29
- }
30
- });
31
-
32
- objectMergeSuite.target('assign').measure('data', (_, input) => {
33
- Object.assign({}, ...input);
34
- });
35
-
36
- const reports = await objectMergeSuite.execute({
37
- reportTypes: ['ops'],
38
- maxCycles: 10_000,
39
- });
40
-
41
- printSimpleReports(reports);
@@ -1,22 +0,0 @@
1
- import { randomUUID } from 'node:crypto';
2
-
3
- const serializeSuite = benchmark('1K strings', () => Array.from({ length: 10_000 }, () => randomUUID()));
4
-
5
- const v8Target = serializeSuite.target('V8', async () => {
6
- const { serialize, deserialize } = await import('node:v8');
7
- const gcBlock = new Set();
8
- return { serialize, deserialize, gcBlock };
9
- });
10
-
11
- v8Target.measure('serialize', ({ serialize, gcBlock }, input) => {
12
- gcBlock.add(serialize(input));
13
- });
14
-
15
- serializeSuite
16
- .target('JSON', () => {
17
- const gcBlock = new Set();
18
- return { gcBlock };
19
- })
20
- .measure('serialize', ({ gcBlock }, input) => {
21
- gcBlock.add(JSON.stringify(input));
22
- });