UNPKG

aiwf

Version:

AI Workflow Framework for Claude Code with multi-language support (Korean/English)

471 lines (392 loc) 13.9 kB
#!/usr/bin/env node import { performance } from 'perf_hooks'; import fs from 'fs/promises'; import path from 'path'; /** * 메모리 프로파일링 및 최적화 시스템 * - 실시간 메모리 사용량 추적 * - 메모리 누수 감지 * - 가비지 컬렉션 최적화 */ class MemoryProfiler { constructor(options = {}) { this.samplingInterval = options.samplingInterval || 1000; // 1초 this.maxSamples = options.maxSamples || 1000; this.thresholds = { heapUsed: options.heapThreshold || 100 * 1024 * 1024, // 100MB heapTotal: options.heapTotalThreshold || 200 * 1024 * 1024, // 200MB external: options.externalThreshold || 50 * 1024 * 1024, // 50MB rss: options.rssThreshold || 500 * 1024 * 1024 // 500MB }; this.samples = []; this.alerts = []; this.profiling = false; this.intervalId = null; this.snapshots = new Map(); this.objectLeakDetector = new ObjectLeakDetector(); } // 프로파일링 시작 startProfiling() { if (this.profiling) { return; } this.profiling = true; this.intervalId = setInterval(() => { this.takeSample(); }, this.samplingInterval); console.log('Memory profiling started'); } // 프로파일링 중지 stopProfiling() { if (!this.profiling) { return; } this.profiling = false; if (this.intervalId) { clearInterval(this.intervalId); this.intervalId = null; } console.log('Memory profiling stopped'); } // 메모리 샘플 수집 takeSample() { const memoryUsage = process.memoryUsage(); const performanceMetrics = this.getPerformanceMetrics(); const sample = { timestamp: Date.now(), memory: memoryUsage, performance: performanceMetrics, gc: this.getGCStats() }; this.samples.push(sample); // 최대 샘플 수 제한 if (this.samples.length > this.maxSamples) { this.samples.shift(); } // 임계값 확인 this.checkThresholds(sample); // 메모리 누수 감지 this.detectMemoryLeaks(sample); } // 성능 메트릭 수집 getPerformanceMetrics() { const entries = performance.getEntriesByType('measure'); const metrics = { totalMeasures: entries.length, averageDuration: 0, maxDuration: 0, minDuration: Infinity }; if (entries.length > 0) { const durations = entries.map(entry => entry.duration); metrics.averageDuration = durations.reduce((a, b) => a + b, 0) / durations.length; metrics.maxDuration = Math.max(...durations); metrics.minDuration = Math.min(...durations); } return metrics; } // GC 통계 수집 getGCStats() { // V8 GC 정보 (Node.js 14+) if (global.gc && typeof global.gc.getHeapStatistics === 'function') { return global.gc.getHeapStatistics(); } return { totalHeapSize: 0, usedHeapSize: 0, heapSizeLimit: 0 }; } // 임계값 확인 checkThresholds(sample) { const { memory } = sample; const alerts = []; Object.entries(this.thresholds).forEach(([key, threshold]) => { if (memory[key] > threshold) { alerts.push({ type: 'threshold_exceeded', metric: key, value: memory[key], threshold, timestamp: sample.timestamp }); } }); if (alerts.length > 0) { this.alerts.push(...alerts); this.triggerAlerts(alerts); } } // 메모리 누수 감지 detectMemoryLeaks(sample) { const recentSamples = this.samples.slice(-10); // 최근 10개 샘플 if (recentSamples.length < 10) { return; } // 메모리 사용량 추세 분석 const heapTrend = this.calculateTrend(recentSamples.map(s => s.memory.heapUsed)); const rssTrend = this.calculateTrend(recentSamples.map(s => s.memory.rss)); // 지속적인 메모리 증가 감지 if (heapTrend > 0.05 || rssTrend > 0.05) { // 5% 이상 증가 this.alerts.push({ type: 'memory_leak_suspected', heapTrend, rssTrend, timestamp: sample.timestamp }); } } // 추세 계산 calculateTrend(values) { if (values.length < 2) return 0; const first = values[0]; const last = values[values.length - 1]; return (last - first) / first; } // 알림 발생 triggerAlerts(alerts) { alerts.forEach(alert => { console.warn(`Memory Alert: ${alert.type}`, alert); }); } // 메모리 스냅샷 생성 createSnapshot(label) { const snapshot = { label, timestamp: Date.now(), memory: process.memoryUsage(), objectCounts: this.objectLeakDetector.getObjectCounts() }; this.snapshots.set(label, snapshot); return snapshot; } // 스냅샷 비교 compareSnapshots(label1, label2) { const snapshot1 = this.snapshots.get(label1); const snapshot2 = this.snapshots.get(label2); if (!snapshot1 || !snapshot2) { throw new Error('Snapshot not found'); } const comparison = { timeDiff: snapshot2.timestamp - snapshot1.timestamp, memoryDiff: {}, objectDiff: {} }; // 메모리 사용량 차이 Object.keys(snapshot1.memory).forEach(key => { comparison.memoryDiff[key] = snapshot2.memory[key] - snapshot1.memory[key]; }); // 객체 수 차이 Object.keys(snapshot1.objectCounts).forEach(key => { comparison.objectDiff[key] = (snapshot2.objectCounts[key] || 0) - (snapshot1.objectCounts[key] || 0); }); return comparison; } // 메모리 최적화 제안 getOptimizationSuggestions() { const suggestions = []; const currentMemory = process.memoryUsage(); const recentSamples = this.samples.slice(-10); // 높은 메모리 사용량 if (currentMemory.heapUsed > this.thresholds.heapUsed) { suggestions.push({ type: 'high_memory_usage', suggestion: 'Consider running garbage collection or optimizing data structures', priority: 'high' }); } // 메모리 누수 의심 if (recentSamples.length > 0) { const heapTrend = this.calculateTrend(recentSamples.map(s => s.memory.heapUsed)); if (heapTrend > 0.1) { suggestions.push({ type: 'memory_leak', suggestion: 'Potential memory leak detected. Review object references and event listeners', priority: 'critical' }); } } // 외부 메모리 사용량 if (currentMemory.external > this.thresholds.external) { suggestions.push({ type: 'external_memory', suggestion: 'High external memory usage. Review Buffer and C++ addon usage', priority: 'medium' }); } return suggestions; } // 가비지 컬렉션 강제 실행 forceGarbageCollection() { if (global.gc) { const beforeGC = process.memoryUsage(); global.gc(); const afterGC = process.memoryUsage(); return { before: beforeGC, after: afterGC, freed: { heapUsed: beforeGC.heapUsed - afterGC.heapUsed, heapTotal: beforeGC.heapTotal - afterGC.heapTotal, external: beforeGC.external - afterGC.external, rss: beforeGC.rss - afterGC.rss } }; } return null; } // 메모리 사용량 보고서 생성 generateReport() { const currentMemory = process.memoryUsage(); const recentSamples = this.samples.slice(-100); // 최근 100개 샘플 const report = { timestamp: Date.now(), current: currentMemory, statistics: this.calculateStatistics(recentSamples), alerts: this.alerts.slice(-10), // 최근 10개 알림 suggestions: this.getOptimizationSuggestions(), snapshots: Array.from(this.snapshots.keys()) }; return report; } // 통계 계산 calculateStatistics(samples) { if (samples.length === 0) { return {}; } const stats = { heapUsed: { min: Infinity, max: 0, avg: 0 }, heapTotal: { min: Infinity, max: 0, avg: 0 }, external: { min: Infinity, max: 0, avg: 0 }, rss: { min: Infinity, max: 0, avg: 0 } }; samples.forEach(sample => { Object.keys(stats).forEach(key => { const value = sample.memory[key]; stats[key].min = Math.min(stats[key].min, value); stats[key].max = Math.max(stats[key].max, value); stats[key].avg += value; }); }); // 평균 계산 Object.keys(stats).forEach(key => { stats[key].avg /= samples.length; }); return stats; } // 보고서를 파일로 저장 async saveReport(filePath) { const report = this.generateReport(); await fs.writeFile(filePath, JSON.stringify(report, null, 2)); return filePath; } // 메모리 사용량 모니터링 monitor(duration = 60000) { // 1분 기본 return new Promise((resolve) => { this.startProfiling(); setTimeout(() => { this.stopProfiling(); resolve(this.generateReport()); }, duration); }); } } // 객체 누수 감지기 class ObjectLeakDetector { constructor() { this.objectCounts = new Map(); this.tracking = false; } startTracking() { this.tracking = true; this.objectCounts.clear(); } stopTracking() { this.tracking = false; } // 객체 수 추적 trackObject(type, operation = 'create') { if (!this.tracking) return; const current = this.objectCounts.get(type) || 0; if (operation === 'create') { this.objectCounts.set(type, current + 1); } else if (operation === 'destroy') { this.objectCounts.set(type, Math.max(0, current - 1)); } } // 현재 객체 수 반환 getObjectCounts() { return Object.fromEntries(this.objectCounts); } // 누수 의심 객체 찾기 findLeakingObjects(threshold = 1000) { const leaking = []; for (const [type, count] of this.objectCounts) { if (count > threshold) { leaking.push({ type, count }); } } return leaking; } } // 메모리 최적화 유틸리티 class MemoryOptimizer { static async optimizeForLargeFiles(filePath, chunkSize = 64 * 1024) { const stat = await fs.stat(filePath); if (stat.size > 100 * 1024 * 1024) { // 100MB 이상 // 스트리밍 처리 권장 return { recommendation: 'streaming', reason: 'File size exceeds 100MB', chunkSize }; } return { recommendation: 'buffer', reason: 'File size is manageable for buffer processing' }; } static optimizeDataStructures(data) { // 메모리 효율적인 데이터 구조 제안 if (Array.isArray(data)) { if (data.length > 10000) { return { recommendation: 'Use Set or Map for better performance', reason: 'Large array detected' }; } } if (typeof data === 'object' && data !== null) { const keys = Object.keys(data); if (keys.length > 1000) { return { recommendation: 'Consider using Map instead of Object', reason: 'Large object with many properties' }; } } return { recommendation: 'Current data structure is optimal', reason: 'Size is within acceptable limits' }; } static async cleanupTemporaryFiles(tmpDir = '/tmp') { const files = await fs.readdir(tmpDir); const cleanedFiles = []; for (const file of files) { const filePath = path.join(tmpDir, file); try { const stat = await fs.stat(filePath); // 1시간 이상 된 임시 파일 삭제 if (Date.now() - stat.mtime.getTime() > 3600000) { await fs.unlink(filePath); cleanedFiles.push(filePath); } } catch (error) { // 파일 삭제 실패는 무시 } } return cleanedFiles; } } export { MemoryProfiler, ObjectLeakDetector, MemoryOptimizer };