Skip to content

Commit 781467d

Browse files
committed
benchmark wip
1 parent 12f3c0c commit 781467d

File tree

7 files changed

+772
-0
lines changed

7 files changed

+772
-0
lines changed

benchmark-results.json

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
{
2+
"files": [
3+
{
4+
"filepath": "/Users/aewing/Projects/phx.digital/language-tools/packages/language-server/test/plugins/typescript/TypeScriptPlugin.bench.ts",
5+
"groups": [
6+
{
7+
"fullName": "packages/language-server/test/plugins/typescript/TypeScriptPlugin.bench.ts > TypeScriptPlugin Performance",
8+
"benchmarks": [
9+
{
10+
"id": "1632965276_0_0",
11+
"name": "document symbols",
12+
"rank": 1,
13+
"rme": 0,
14+
"samples": []
15+
},
16+
{
17+
"id": "1632965276_0_1",
18+
"name": "definitions",
19+
"rank": 2,
20+
"rme": 0,
21+
"samples": []
22+
},
23+
{
24+
"id": "1632965276_0_2",
25+
"name": "hover",
26+
"rank": 3,
27+
"rme": 0,
28+
"samples": []
29+
},
30+
{
31+
"id": "1632965276_0_3",
32+
"name": "diagnostics",
33+
"rank": 4,
34+
"rme": 0,
35+
"samples": []
36+
},
37+
{
38+
"id": "1632965276_0_4",
39+
"name": "completions",
40+
"rank": 5,
41+
"rme": 0,
42+
"samples": []
43+
},
44+
{
45+
"id": "1632965276_0_5",
46+
"name": "find references",
47+
"rank": 6,
48+
"rme": 0,
49+
"samples": []
50+
},
51+
{
52+
"id": "1632965276_0_6",
53+
"name": "semantic tokens",
54+
"rank": 7,
55+
"rme": 0,
56+
"samples": []
57+
},
58+
{
59+
"id": "1632965276_0_7",
60+
"name": "code actions",
61+
"rank": 8,
62+
"rme": 0,
63+
"samples": []
64+
},
65+
{
66+
"id": "1632965276_0_8",
67+
"name": "rename",
68+
"rank": 9,
69+
"rme": 0,
70+
"samples": []
71+
},
72+
{
73+
"id": "1632965276_0_9",
74+
"name": "signature help",
75+
"rank": 10,
76+
"rme": 0,
77+
"samples": []
78+
}
79+
]
80+
}
81+
]
82+
},
83+
{
84+
"filepath": "/Users/aewing/Projects/phx.digital/language-tools/packages/language-server/test/plugins/typescript/features/CompletionProvider.bench.ts",
85+
"groups": [
86+
{
87+
"fullName": "packages/language-server/test/plugins/typescript/features/CompletionProvider.bench.ts > CompletionProvider Performance",
88+
"benchmarks": [
89+
{
90+
"id": "-1725875069_0_0",
91+
"name": "variable completions",
92+
"rank": 1,
93+
"rme": 0,
94+
"samples": []
95+
},
96+
{
97+
"id": "-1725875069_0_1",
98+
"name": "dot completions",
99+
"rank": 2,
100+
"rme": 0,
101+
"samples": []
102+
},
103+
{
104+
"id": "-1725875069_0_2",
105+
"name": "import completions",
106+
"rank": 3,
107+
"rme": 0,
108+
"samples": []
109+
},
110+
{
111+
"id": "-1725875069_0_3",
112+
"name": "component event completions",
113+
"rank": 4,
114+
"rme": 0,
115+
"samples": []
116+
}
117+
]
118+
}
119+
]
120+
},
121+
{
122+
"filepath": "/Users/aewing/Projects/phx.digital/language-tools/packages/language-server/test/plugins/typescript/features/DiagnosticsProvider.bench.ts",
123+
"groups": [
124+
{
125+
"fullName": "packages/language-server/test/plugins/typescript/features/DiagnosticsProvider.bench.ts > DiagnosticsProvider Performance",
126+
"benchmarks": [
127+
{
128+
"id": "-1392187383_0_0",
129+
"name": "diagnostics small file",
130+
"rank": 1,
131+
"rme": 0,
132+
"samples": []
133+
},
134+
{
135+
"id": "-1392187383_0_1",
136+
"name": "diagnostics large file",
137+
"rank": 2,
138+
"rme": 0,
139+
"samples": []
140+
},
141+
{
142+
"id": "-1392187383_0_2",
143+
"name": "rapid diagnostics",
144+
"rank": 3,
145+
"rme": 0,
146+
"samples": []
147+
},
148+
{
149+
"id": "-1392187383_0_3",
150+
"name": "semantic diagnostics",
151+
"rank": 4,
152+
"rme": 0,
153+
"samples": []
154+
}
155+
]
156+
}
157+
]
158+
},
159+
{
160+
"filepath": "/Users/aewing/Projects/phx.digital/language-tools/packages/language-server/test/plugins/typescript/features/HoverProvider.bench.ts",
161+
"groups": [
162+
{
163+
"fullName": "packages/language-server/test/plugins/typescript/features/HoverProvider.bench.ts > HoverProvider Performance",
164+
"benchmarks": [
165+
{
166+
"id": "-1327127879_0_0",
167+
"name": "hover on variable",
168+
"rank": 1,
169+
"rme": 0,
170+
"samples": []
171+
},
172+
{
173+
"id": "-1327127879_0_1",
174+
"name": "hover on function",
175+
"rank": 2,
176+
"rme": 0,
177+
"samples": []
178+
},
179+
{
180+
"id": "-1327127879_0_2",
181+
"name": "hover on import",
182+
"rank": 3,
183+
"rme": 0,
184+
"samples": []
185+
},
186+
{
187+
"id": "-1327127879_0_3",
188+
"name": "hover on component prop",
189+
"rank": 4,
190+
"rme": 0,
191+
"samples": []
192+
}
193+
]
194+
}
195+
]
196+
}
197+
]
198+
}
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
#!/usr/bin/env node
2+
3+
/**
4+
* Simple benchmark comparing local built language server vs npm latest
5+
* Run with: node benchmarks/language-server-comparison.js
6+
*/
7+
8+
const { spawn } = require('child_process');
9+
const { performance } = require('perf_hooks');
10+
const fs = require('fs');
11+
const path = require('path');
12+
13+
async function runCommand(command, args, cwd) {
14+
return new Promise((resolve, reject) => {
15+
const proc = spawn(command, args, { cwd, stdio: 'pipe' });
16+
let stdout = '';
17+
let stderr = '';
18+
19+
proc.stdout.on('data', (data) => stdout += data);
20+
proc.stderr.on('data', (data) => stderr += data);
21+
22+
proc.on('close', (code) => {
23+
if (code === 0) {
24+
resolve({ stdout, stderr });
25+
} else {
26+
reject(new Error(`Command failed: ${stderr}`));
27+
}
28+
});
29+
});
30+
}
31+
32+
async function installNpmLatest() {
33+
console.log('📦 Installing svelte-language-server@latest...');
34+
try {
35+
await runCommand('npm', ['install', 'svelte-language-server@latest'],
36+
path.join(__dirname, 'temp'));
37+
} catch (error) {
38+
// Create temp dir and try again
39+
fs.mkdirSync(path.join(__dirname, 'temp'), { recursive: true });
40+
fs.writeFileSync(path.join(__dirname, 'temp', 'package.json'), '{}');
41+
await runCommand('npm', ['install', 'svelte-language-server@latest'],
42+
path.join(__dirname, 'temp'));
43+
}
44+
}
45+
46+
async function benchmarkLanguageServer(serverPath, name) {
47+
console.log(`\n🔥 Benchmarking ${name}...`);
48+
49+
const testFile = path.join(__dirname, 'test-file.svelte');
50+
if (!fs.existsSync(testFile)) {
51+
fs.writeFileSync(testFile, `<script lang="ts">
52+
let count: number = 0;
53+
let name: string = 'world';
54+
55+
function increment() {
56+
count += 1;
57+
}
58+
59+
$: doubled = count * 2;
60+
</script>
61+
62+
<h1>Hello {name}!</h1>
63+
<button on:click={increment}>
64+
Count: {count} (doubled: {doubled})
65+
</button>
66+
67+
<style>
68+
h1 { color: blue; }
69+
button { padding: 10px; }
70+
</style>`);
71+
}
72+
73+
const results = {};
74+
75+
// Test basic operations by sending LSP requests
76+
// This is simplified - in reality you'd send proper LSP initialize/requests
77+
78+
try {
79+
// Measure startup time
80+
const startTime = performance.now();
81+
const proc = spawn('node', [serverPath, '--stdio']);
82+
83+
// Give it a moment to start
84+
await new Promise(resolve => setTimeout(resolve, 100));
85+
86+
const initTime = performance.now() - startTime;
87+
results.startup = `${initTime.toFixed(1)}ms`;
88+
89+
proc.kill();
90+
91+
// For now, just measure the module require time as a proxy
92+
const requireStart = performance.now();
93+
delete require.cache[require.resolve(serverPath)];
94+
require(serverPath);
95+
const requireTime = performance.now() - requireStart;
96+
results.moduleLoad = `${requireTime.toFixed(1)}ms`;
97+
98+
} catch (error) {
99+
results.error = error.message;
100+
}
101+
102+
return results;
103+
}
104+
105+
async function main() {
106+
console.log('🏁 Language Server Version Comparison');
107+
console.log('=====================================\n');
108+
109+
// Setup
110+
await installNpmLatest();
111+
112+
// Build local version
113+
console.log('🔨 Building local version...');
114+
await runCommand('npm', ['run', 'build'], path.join(__dirname, '..', 'packages', 'language-server'));
115+
116+
// Benchmark paths
117+
const localPath = path.join(__dirname, '..', 'packages', 'language-server', 'dist', 'src', 'index.js');
118+
const npmPath = path.join(__dirname, 'temp', 'node_modules', 'svelte-language-server', 'dist', 'src', 'index.js');
119+
120+
// Check if files exist
121+
if (!fs.existsSync(localPath)) {
122+
console.error('❌ Local build not found. Run `npm run build` first.');
123+
return;
124+
}
125+
126+
if (!fs.existsSync(npmPath)) {
127+
console.error('❌ npm version not found. Installation failed.');
128+
return;
129+
}
130+
131+
// Get versions
132+
const localPkg = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'packages', 'language-server', 'package.json')));
133+
const npmPkg = JSON.parse(fs.readFileSync(path.join(__dirname, 'temp', 'node_modules', 'svelte-language-server', 'package.json')));
134+
135+
console.log(`📊 Comparing versions:`);
136+
console.log(` Local: ${localPkg.version}`);
137+
console.log(` npm: ${npmPkg.version}`);
138+
139+
// Run benchmarks
140+
const localResults = await benchmarkLanguageServer(localPath, `Local v${localPkg.version}`);
141+
const npmResults = await benchmarkLanguageServer(npmPath, `npm v${npmPkg.version}`);
142+
143+
// Display results
144+
console.log('\n📈 Results:');
145+
console.log('===========');
146+
console.log('| Metric | Local | npm | Comparison |');
147+
console.log('|--------|--------|--------|------------|');
148+
149+
for (const [metric, localValue] of Object.entries(localResults)) {
150+
const npmValue = npmResults[metric] || 'N/A';
151+
const localMs = parseFloat(localValue);
152+
const npmMs = parseFloat(npmValue);
153+
154+
let comparison = 'N/A';
155+
if (!isNaN(localMs) && !isNaN(npmMs)) {
156+
if (localMs < npmMs) {
157+
const improvement = ((npmMs - localMs) / npmMs * 100).toFixed(1);
158+
comparison = `🚀 ${improvement}% faster`;
159+
} else if (localMs > npmMs) {
160+
const regression = ((localMs - npmMs) / npmMs * 100).toFixed(1);
161+
comparison = `🐌 ${regression}% slower`;
162+
} else {
163+
comparison = '⚖️ same';
164+
}
165+
}
166+
167+
console.log(`| ${metric} | ${localValue} | ${npmValue} | ${comparison} |`);
168+
}
169+
170+
console.log('\n✅ Benchmark complete!');
171+
}
172+
173+
main().catch(console.error);

benchmarks/vitest.config.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { defineConfig } from 'vitest/config';
2+
import path from 'path';
3+
4+
export default defineConfig({
5+
benchmark: {
6+
include: ['**/*.bench.ts'],
7+
outputFile: './benchmark-results.json',
8+
reporters: ['verbose']
9+
},
10+
resolve: {
11+
alias: {
12+
'@': path.resolve(__dirname, '../packages/language-server/src')
13+
}
14+
}
15+
});

0 commit comments

Comments
 (0)