Skip to main content
Glama

documcp

by tosin2013
performance.ts9.48 kB
// Performance benchmarking system per PERF-001 rules import { promises as fs } from 'fs'; import path from 'path'; import { analyzeRepository } from '../tools/analyze-repository.js'; export interface BenchmarkResult { repoSize: 'small' | 'medium' | 'large'; fileCount: number; executionTime: number; targetTime: number; passed: boolean; performanceRatio: number; details: { startTime: number; endTime: number; memoryUsage: NodeJS.MemoryUsage; }; } export interface BenchmarkSuite { testName: string; results: BenchmarkResult[]; overallPassed: boolean; averagePerformance: number; summary: { smallRepos: { count: number; avgTime: number; passed: number }; mediumRepos: { count: number; avgTime: number; passed: number }; largeRepos: { count: number; avgTime: number; passed: number }; }; } // PERF-001 performance targets const PERFORMANCE_TARGETS = { small: 1000, // <1 second for <100 files medium: 10000, // <10 seconds for 100-1000 files large: 60000, // <60 seconds for 1000+ files } as const; export class PerformanceBenchmarker { private results: BenchmarkResult[] = []; async benchmarkRepository( repoPath: string, depth: 'quick' | 'standard' | 'deep' = 'standard', ): Promise<BenchmarkResult> { const fileCount = await this.getFileCount(repoPath); const repoSize = this.categorizeRepoSize(fileCount); const targetTime = PERFORMANCE_TARGETS[repoSize]; // Capture initial memory state const initialMemory = process.memoryUsage(); const startTime = Date.now(); try { // Run the actual analysis await analyzeRepository({ path: repoPath, depth }); const endTime = Date.now(); const executionTime = endTime - startTime; const finalMemory = process.memoryUsage(); const performanceRatio = executionTime / targetTime; const passed = executionTime <= targetTime; const result: BenchmarkResult = { repoSize, fileCount, executionTime, targetTime, passed, performanceRatio, details: { startTime, endTime, memoryUsage: { rss: finalMemory.rss - initialMemory.rss, heapTotal: finalMemory.heapTotal - initialMemory.heapTotal, heapUsed: finalMemory.heapUsed - initialMemory.heapUsed, external: finalMemory.external - initialMemory.external, arrayBuffers: finalMemory.arrayBuffers - initialMemory.arrayBuffers, }, }, }; this.results.push(result); return result; } catch (error) { const endTime = Date.now(); const executionTime = endTime - startTime; // Even failed executions should be benchmarked const result: BenchmarkResult = { repoSize, fileCount, executionTime, targetTime, passed: false, // Failed execution = failed performance performanceRatio: executionTime / targetTime, details: { startTime, endTime, memoryUsage: process.memoryUsage(), }, }; this.results.push(result); throw error; } } async runBenchmarkSuite( testRepos: Array<{ path: string; name: string }>, ): Promise<BenchmarkSuite> { console.log('🚀 Starting performance benchmark suite...\n'); const results: BenchmarkResult[] = []; for (const repo of testRepos) { console.log(`📊 Benchmarking: ${repo.name}`); try { const result = await this.benchmarkRepository(repo.path); results.push(result); const status = result.passed ? '✅ PASS' : '❌ FAIL'; const ratio = (result.performanceRatio * 100).toFixed(1); console.log( ` ${status} ${result.executionTime}ms (${ratio}% of target) - ${result.repoSize} repo with ${result.fileCount} files`, ); } catch (error) { console.log(` ❌ ERROR: ${error}`); } } console.log('\n📈 Generating performance summary...\n'); return this.generateSuite('Full Benchmark Suite', results); } generateSuite(testName: string, results: BenchmarkResult[]): BenchmarkSuite { const overallPassed = results.every((r) => r.passed); const averagePerformance = results.reduce((sum, r) => sum + r.performanceRatio, 0) / results.length; // Categorize results const smallRepos = results.filter((r) => r.repoSize === 'small'); const mediumRepos = results.filter((r) => r.repoSize === 'medium'); const largeRepos = results.filter((r) => r.repoSize === 'large'); const suite: BenchmarkSuite = { testName, results, overallPassed, averagePerformance, summary: { smallRepos: { count: smallRepos.length, avgTime: smallRepos.reduce((sum, r) => sum + r.executionTime, 0) / smallRepos.length || 0, passed: smallRepos.filter((r) => r.passed).length, }, mediumRepos: { count: mediumRepos.length, avgTime: mediumRepos.reduce((sum, r) => sum + r.executionTime, 0) / mediumRepos.length || 0, passed: mediumRepos.filter((r) => r.passed).length, }, largeRepos: { count: largeRepos.length, avgTime: largeRepos.reduce((sum, r) => sum + r.executionTime, 0) / largeRepos.length || 0, passed: largeRepos.filter((r) => r.passed).length, }, }, }; return suite; } printDetailedReport(suite: BenchmarkSuite): void { console.log(`📋 Performance Benchmark Report: ${suite.testName}`); console.log('='.repeat(60)); console.log(`Overall Status: ${suite.overallPassed ? '✅ PASSED' : '❌ FAILED'}`); console.log(`Average Performance: ${(suite.averagePerformance * 100).toFixed(1)}% of target`); console.log(`Total Tests: ${suite.results.length}\n`); // Summary by repo size console.log('📊 Performance by Repository Size:'); console.log('-'.repeat(40)); const categories = [ { name: 'Small (<100 files)', data: suite.summary.smallRepos, target: PERFORMANCE_TARGETS.small, }, { name: 'Medium (100-1000 files)', data: suite.summary.mediumRepos, target: PERFORMANCE_TARGETS.medium, }, { name: 'Large (1000+ files)', data: suite.summary.largeRepos, target: PERFORMANCE_TARGETS.large, }, ]; categories.forEach((cat) => { if (cat.data.count > 0) { const passRate = ((cat.data.passed / cat.data.count) * 100).toFixed(1); const avgTime = cat.data.avgTime.toFixed(0); const targetTime = (cat.target / 1000).toFixed(1); console.log(`${cat.name}:`); console.log( ` Tests: ${cat.data.count} | Passed: ${cat.data.passed}/${cat.data.count} (${passRate}%)`, ); console.log(` Avg Time: ${avgTime}ms | Target: <${targetTime}s`); console.log(''); } }); // Detailed results console.log('🔍 Detailed Results:'); console.log('-'.repeat(40)); suite.results.forEach((result, i) => { const status = result.passed ? '✅' : '❌'; const ratio = (result.performanceRatio * 100).toFixed(1); const memoryMB = (result.details.memoryUsage.heapUsed / 1024 / 1024).toFixed(1); console.log(`${status} Test ${i + 1}: ${result.executionTime}ms (${ratio}% of target)`); console.log( ` Size: ${result.repoSize} (${result.fileCount} files) | Memory: ${memoryMB}MB heap`, ); }); console.log('\n' + '='.repeat(60)); } exportResults(suite: BenchmarkSuite, outputPath: string): Promise<void> { const report = { timestamp: new Date().toISOString(), suite, systemInfo: { node: process.version, platform: process.platform, arch: process.arch, memoryUsage: process.memoryUsage(), }, performanceTargets: PERFORMANCE_TARGETS, }; return fs.writeFile(outputPath, JSON.stringify(report, null, 2)); } private async getFileCount(repoPath: string): Promise<number> { let fileCount = 0; async function countFiles(dir: string, level = 0): Promise<void> { if (level > 10) return; // Prevent infinite recursion try { const entries = await fs.readdir(dir, { withFileTypes: true }); for (const entry of entries) { if (entry.name.startsWith('.') && entry.name !== '.github') continue; if (entry.name === 'node_modules' || entry.name === 'vendor') continue; const fullPath = path.join(dir, entry.name); if (entry.isDirectory()) { await countFiles(fullPath, level + 1); } else { fileCount++; } } } catch (error) { // Skip inaccessible directories } } await countFiles(repoPath); return fileCount; } private categorizeRepoSize(fileCount: number): 'small' | 'medium' | 'large' { if (fileCount < 100) return 'small'; if (fileCount < 1000) return 'medium'; return 'large'; } // Utility method to clear results for fresh benchmarking reset(): void { this.results = []; } // Get current benchmark results getResults(): BenchmarkResult[] { return [...this.results]; } } // Factory function for easy usage export function createBenchmarker(): PerformanceBenchmarker { return new PerformanceBenchmarker(); }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/tosin2013/documcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server