Spaces:
Sleeping
Sleeping
| /** | |
| * パフォーマンス最適化ユーティリティ | |
| * 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<string, unknown>)[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<T, R>( | |
| items: T[], | |
| processor: (item: T) => Promise<R>, | |
| chunkSize: number = 10, | |
| delayMs: number = 100, | |
| ): Promise<R[]> { | |
| 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<T extends (...args: unknown[]) => unknown>( | |
| func: T, | |
| wait: number, | |
| ): (...args: Parameters<T>) => void { | |
| let timeout: NodeJS.Timeout | null = null; | |
| return function (...args: Parameters<T>) { | |
| if (timeout) clearTimeout(timeout); | |
| timeout = setTimeout(() => { | |
| func(...args); | |
| }, wait); | |
| }; | |
| } | |
| // スロットリング(一定時間内の呼び出し回数を制限) | |
| export function throttle<T extends (...args: unknown[]) => unknown>( | |
| func: T, | |
| limit: number, | |
| ): (...args: Parameters<T>) => void { | |
| let inThrottle = false; | |
| return function (...args: Parameters<T>) { | |
| if (!inThrottle) { | |
| func(...args); | |
| inThrottle = true; | |
| setTimeout(() => { | |
| inThrottle = false; | |
| }, limit); | |
| } | |
| }; | |
| } | |
| // メモリ効率的な画像処理のためのキャッシュ管理 | |
| class ImageCache { | |
| private cache: Map<string, { data: string; timestamp: number }> = 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<T>(task: () => Promise<T>): Promise<T> { | |
| while (this.running >= this.maxConcurrent) { | |
| await new Promise<void>((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<string, number> = 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<T>(label: string, fn: () => T): T { | |
| this.start(label); | |
| try { | |
| return fn(); | |
| } finally { | |
| this.end(label); | |
| } | |
| } | |
| async measureAsync<T>(label: string, fn: () => Promise<T>): Promise<T> { | |
| this.start(label); | |
| try { | |
| return await fn(); | |
| } finally { | |
| this.end(label); | |
| } | |
| } | |
| } | |
| export const performanceMonitor = new PerformanceMonitor(); | |