|
| 1 | +import fs from 'node:fs/promises'; |
| 2 | +import os from 'node:os'; |
| 3 | +import path from 'node:path'; |
| 4 | +import { performance } from 'node:perf_hooks'; |
| 5 | +import { buildContext } from '../src/core/context.js'; |
| 6 | +import { buildIndex } from '../src/core/indexer.js'; |
| 7 | +import { projectMap } from '../src/core/map.js'; |
| 8 | +import { readCode } from '../src/core/reader.js'; |
| 9 | +import { renderSkeleton, skeletonPath } from '../src/core/skeleton.js'; |
| 10 | + |
| 11 | +const root = await fs.mkdtemp(path.join(os.tmpdir(), 'codebone-bench-')); |
| 12 | +await fs.mkdir(path.join(root, 'src')); |
| 13 | +const file = 'src/server.ts'; |
| 14 | +await fs.writeFile(path.join(root, file), ` |
| 15 | +import { Router } from './router'; |
| 16 | +
|
| 17 | +export class Server { |
| 18 | + private router: Router; |
| 19 | +
|
| 20 | + constructor(router: Router) { |
| 21 | + this.router = router; |
| 22 | + } |
| 23 | +
|
| 24 | + async start(): Promise<void> { |
| 25 | +${Array.from({ length: 80 }, (_, index) => ` await this.router.handleRequest('/${index}');`).join('\n')} |
| 26 | + } |
| 27 | +} |
| 28 | +
|
| 29 | +export function createServer(router: Router): Server { |
| 30 | +${Array.from({ length: 30 }, () => ' const server = new Server(router);').join('\n')} |
| 31 | + return new Server(router); |
| 32 | +} |
| 33 | +`); |
| 34 | +await fs.writeFile(path.join(root, 'src/router.ts'), 'export class Router { async handleRequest(path: string): Promise<string> { return path; } }\n'); |
| 35 | +for (let index = 0; index < 150; index += 1) { |
| 36 | + await fs.writeFile(path.join(root, 'src', `feature-${index}.ts`), `import { Router } from './router';\nexport function feature${index}(router: Router): Promise<string> {\n return router.handleRequest('/feature-${index}');\n}\n`); |
| 37 | +} |
| 38 | + |
| 39 | +const skeleton = await measure('skeleton file', 250, () => skeletonPath(root, file)); |
| 40 | +await measure('read symbol', 150, () => readCode(root, file, { symbol: 'createServer' })); |
| 41 | +await measure('context pack', 3000, () => buildContext(root, { goal: 'server request handling', budget: 4000 })); |
| 42 | +await measure('project map 150 files', 2500, () => projectMap(root, '.', 1200)); |
| 43 | +await measure('index 150 files', 4000, () => buildIndex(root, '.')); |
| 44 | + |
| 45 | +const fullSource = await fs.readFile(path.join(root, file), 'utf8'); |
| 46 | +const compression = fullSource.length / Math.max(1, renderSkeleton(skeleton.result).length); |
| 47 | +if (compression < 1.2) throw new Error(`Token compression smoke failed: ${compression.toFixed(2)}x`); |
| 48 | + |
| 49 | +process.stdout.write(`Smoke benchmarks ok\n files: 152\n skeleton: ${skeleton.elapsedMs.toFixed(1)}ms\n compression: ${compression.toFixed(2)}x\n`); |
| 50 | + |
| 51 | +async function measure<T>(name: string, maxMs: number, fn: () => Promise<T>): Promise<{ result: T; elapsedMs: number }> { |
| 52 | + const started = performance.now(); |
| 53 | + const result = await fn(); |
| 54 | + const elapsedMs = performance.now() - started; |
| 55 | + if (elapsedMs > maxMs) throw new Error(`${name} SLO failed: ${elapsedMs.toFixed(1)}ms > ${maxMs}ms`); |
| 56 | + return { result, elapsedMs }; |
| 57 | +} |
0 commit comments