/** * パフォーマンス最適化ユーティリティ * HuggingFace Spaces制限に対応したメモリ管理とガベージコレクション */ // メモリ使用量の監視と報告 export function getMemoryUsage(): { used: number; total: number; percentage: number; } { if (typeof process !== 'undefined' && process.memoryUsage) { const usage = process.memoryUsage(); const total = usage.heapTotal; const used = usage.heapUsed; return { used, total, percentage: (used / total) * 100, }; } return { used: 0, total: 0, percentage: 0 }; } // メモリ閾値のチェック(デフォルト: 80%) export function isMemoryThresholdExceeded(threshold = 0.8): boolean { const { percentage } = getMemoryUsage(); return percentage > threshold * 100; } // 強制的なガベージコレクション(Node.js環境でのみ動作) export function forceGarbageCollection(): void { if (typeof global !== 'undefined' && global.gc) { console.log('Forcing garbage collection...'); global.gc(); } } // 大きな画像データのクリーンアップ export function cleanupImageData(imageData: unknown): void { if (imageData && typeof imageData === 'object') { // ImageDataやBufferの参照を解放 Object.keys(imageData).forEach((key) => { delete (imageData as Record)[key]; }); } } // Base64画像のサイズ最適化 export function optimizeBase64Image( base64: string, maxSizeKB: number = 1024, ): string { // Base64のサイズを推定(約1.37倍のオーバーヘッド) const estimatedSizeKB = (base64.length * 0.75) / 1024; if (estimatedSizeKB > maxSizeKB) { console.warn( `Image size (${estimatedSizeKB.toFixed(2)}KB) exceeds limit (${maxSizeKB}KB)`, ); // TODO: 実際の画像圧縮処理を実装 } return base64; } // 処理の分割実行(大きなタスクを小さなチャンクに分割) export async function processInChunks( items: T[], processor: (item: T) => Promise, chunkSize: number = 10, delayMs: number = 100, ): Promise { const results: R[] = []; for (let i = 0; i < items.length; i += chunkSize) { const chunk = items.slice(i, i + chunkSize); const chunkResults = await Promise.all(chunk.map(processor)); results.push(...chunkResults); // メモリチェックとガベージコレクション if (isMemoryThresholdExceeded(0.7)) { forceGarbageCollection(); await new Promise((resolve) => setTimeout(resolve, delayMs * 2)); } else if (i + chunkSize < items.length) { // 次のチャンクの前に短い遅延 await new Promise((resolve) => setTimeout(resolve, delayMs)); } } return results; } // デバウンス処理(連続した呼び出しを制限) export function debounce unknown>( func: T, wait: number, ): (...args: Parameters) => void { let timeout: NodeJS.Timeout | null = null; return function (...args: Parameters) { if (timeout) clearTimeout(timeout); timeout = setTimeout(() => { func(...args); }, wait); }; } // スロットリング(一定時間内の呼び出し回数を制限) export function throttle unknown>( func: T, limit: number, ): (...args: Parameters) => void { let inThrottle = false; return function (...args: Parameters) { if (!inThrottle) { func(...args); inThrottle = true; setTimeout(() => { inThrottle = false; }, limit); } }; } // メモリ効率的な画像処理のためのキャッシュ管理 class ImageCache { private cache: Map = new Map(); private maxSize: number; private ttl: number; constructor(maxSize: number = 10, ttlMinutes: number = 5) { this.maxSize = maxSize; this.ttl = ttlMinutes * 60 * 1000; } set(key: string, data: string): void { // 古いエントリの削除 this.cleanup(); // キャッシュサイズ制限 if (this.cache.size >= this.maxSize) { const oldestKey = Array.from(this.cache.entries()).sort( (a, b) => a[1].timestamp - b[1].timestamp, )[0][0]; this.cache.delete(oldestKey); } this.cache.set(key, { data, timestamp: Date.now() }); } get(key: string): string | null { const entry = this.cache.get(key); if (!entry) return null; // TTLチェック if (Date.now() - entry.timestamp > this.ttl) { this.cache.delete(key); return null; } return entry.data; } cleanup(): void { const now = Date.now(); for (const [key, entry] of this.cache.entries()) { if (now - entry.timestamp > this.ttl) { this.cache.delete(key); } } } clear(): void { this.cache.clear(); } } export const imageCache = new ImageCache(); // 同時処理数の制限 export class ConcurrencyLimiter { private running: number = 0; private queue: Array<() => void> = []; private maxConcurrent: number; constructor(maxConcurrent: number = 3) { this.maxConcurrent = maxConcurrent; } async execute(task: () => Promise): Promise { while (this.running >= this.maxConcurrent) { await new Promise((resolve) => { this.queue.push(resolve); }); } this.running++; try { return await task(); } finally { this.running--; const next = this.queue.shift(); if (next) next(); } } } // パフォーマンス監視 export class PerformanceMonitor { private timings: Map = new Map(); start(label: string): void { this.timings.set(label, performance.now()); } end(label: string): number { const start = this.timings.get(label); if (!start) { console.warn(`No start time found for label: ${label}`); return 0; } const duration = performance.now() - start; this.timings.delete(label); console.log(`[Performance] ${label}: ${duration.toFixed(2)}ms`); return duration; } measure(label: string, fn: () => T): T { this.start(label); try { return fn(); } finally { this.end(label); } } async measureAsync(label: string, fn: () => Promise): Promise { this.start(label); try { return await fn(); } finally { this.end(label); } } } export const performanceMonitor = new PerformanceMonitor();