diff --git a/assets/libs/ecs/ECS.ts b/assets/libs/ecs/ECS.ts index 6a63b49..e9c465a 100644 --- a/assets/libs/ecs/ECS.ts +++ b/assets/libs/ecs/ECS.ts @@ -5,7 +5,7 @@ import { ECSMatcher } from './ECSMatcher'; import type { CompCtor, CompType, EntityCtor } from './ECSModel'; import { ECSModel } from './ECSModel'; import { ECSComblockSystem, ECSRootSystem, ECSSystem } from './ECSSystem'; -import { globalPoolCoordinator } from './ECSPoolManager'; +import { ecsPoolCoordinator } from './ECSPoolManager'; /** * ECSEntity对象在destroy后,会回收到ECSModel.entityPool实体对象池中 @@ -181,15 +181,14 @@ export namespace ecs { } // 使用动态池管理器 - const pool = globalPoolCoordinator.getPool( + const pool = ecsPoolCoordinator.getPool( entityName, () => { const entity = new ctor(); entity.eid = ECSModel.eid++; // 实体唯一编号 entity.name = entityName; return entity; - }, - ctor + } ); const entity = pool.get(); @@ -231,9 +230,6 @@ export namespace ecs { }); ECSModel.eid2Entity.clear(); ECSModel.groups.clear(); - - // 保存历史数据用于下次学习 - globalPoolCoordinator.saveHistory(); } /** @@ -256,7 +252,7 @@ export namespace ecs { ECSMask.clearPool(); // 清理动态池管理器 - globalPoolCoordinator.getSmartManager().clearAll(); + ecsPoolCoordinator.clearAll(); } /** diff --git a/assets/libs/ecs/ECSEntity.ts b/assets/libs/ecs/ECSEntity.ts index fd191d6..c0eac2b 100644 --- a/assets/libs/ecs/ECSEntity.ts +++ b/assets/libs/ecs/ECSEntity.ts @@ -2,7 +2,7 @@ import type { ecs } from './ECS'; import { ECSMask } from './ECSMask'; import type { CompCtor, CompType } from './ECSModel'; import { ECSModel } from './ECSModel'; -import { globalPoolCoordinator } from './ECSPoolManager'; +import { ecsPoolCoordinator } from './ECSPoolManager'; //#region 辅助方法 @@ -56,10 +56,9 @@ function createComp(ctor: CompCtor): T { } // 使用动态池管理器 - const pool = globalPoolCoordinator.getPool( + const pool = ecsPoolCoordinator.getPool( ctor.compName, - () => new (cct as CompCtor)(), - ctor + () => new (cct as CompCtor)() ); const component = pool.get(); @@ -75,7 +74,7 @@ function createComp(ctor: CompCtor): T { function destroyEntity(entity: ECSEntity): void { if (ECSModel.eid2Entity.has(entity.eid)) { // 使用动态池管理器 - const pool = globalPoolCoordinator.getPool( + const pool = ecsPoolCoordinator.getPool( entity.name, () => new ECSEntity() ); @@ -347,10 +346,9 @@ export class ECSEntity { if (comp.canRecycle) { const ctor = ECSModel.compCtors[componentTypeId]; if (ctor) { - const pool = globalPoolCoordinator.getPool( + const pool = ecsPoolCoordinator.getPool( ctor.compName, - () => new (ctor as any)(), - ctor + () => new (ctor as any)() ); pool.recycle(comp); } @@ -386,10 +384,9 @@ export class ECSEntity { comp.reset(); if (comp.canRecycle) { if (ctorObj) { - const pool = globalPoolCoordinator.getPool( + const pool = ecsPoolCoordinator.getPool( ctorObj.compName, - () => new (ctorObj as any)(), - ctorObj + () => new (ctorObj as any)() ); pool.recycle(comp); } @@ -412,10 +409,9 @@ export class ECSEntity { if (comp.canRecycle) { const ctor = ECSModel.compCtors[tid]; if (ctor) { - const pool = globalPoolCoordinator.getPool( + const pool = ecsPoolCoordinator.getPool( ctor.compName, - () => new (ctor as any)(), - ctor + () => new (ctor as any)() ); pool.recycle(comp); } diff --git a/assets/libs/ecs/ECSMemoryMonitor.ts b/assets/libs/ecs/ECSMemoryMonitor.ts deleted file mode 100644 index a9f34cf..0000000 --- a/assets/libs/ecs/ECSMemoryMonitor.ts +++ /dev/null @@ -1,464 +0,0 @@ -/* - * ECS内存池监控工具 - * 用于实时监控和诊断对象池的使用情况 - */ - -import { globalPoolCoordinator } from './ECSPoolManager'; - -/** 内存监控统计 */ -export interface MemoryStats { - poolName: string; - currentSize: number; - maxSize: number; - minSize: number; - hitRate: number; - createCount: number; - recycleCount: number; - createFrequency: number; - accessFrequency: number; - estimatedMemory: string; - memoryBytes: number; - hasIssue: boolean; - issueType?: 'low-hit-rate' | 'memory-leak' | 'over-capacity' | 'unused'; -} - -/** 监控配置 */ -export interface MonitorConfig { - /** 详细程度:compact(简洁) | normal(普通) | detailed(详细) */ - level?: 'compact' | 'normal' | 'detailed'; - /** 是否显示异常池 */ - showIssues?: boolean; - /** 是否显示内存信息 */ - showMemory?: boolean; - /** 是否按某个字段排序 */ - sortBy?: 'frequency' | 'hitRate' | 'memory' | 'name'; -} - -/** 异常检测阈值 */ -interface IssueThresholds { - lowHitRate: number; // 低命中率阈值(默认50%) - highCapacity: number; // 高容量使用率阈值(默认90%) - unusedTime: number; // 未使用时间阈值(默认60秒) - memoryLeakGrowth: number; // 内存泄漏增长率阈值(默认200%) -} - -/** - * ECS内存监控器 - */ -export class ECSMemoryMonitor { - private static instance: ECSMemoryMonitor; - private updateInterval: number = 1000; // 1秒更新一次 - private isMonitoring = false; - private timerId: any = null; - private lastStats: Map = new Map(); - - private thresholds: IssueThresholds = { - lowHitRate: 50, - highCapacity: 90, - unusedTime: 60000, - memoryLeakGrowth: 200 - }; - - private constructor() { } - - static getInstance(): ECSMemoryMonitor { - if (!ECSMemoryMonitor.instance) { - ECSMemoryMonitor.instance = new ECSMemoryMonitor(); - } - return ECSMemoryMonitor.instance; - } - - /** 设置异常检测阈值 */ - setThresholds(thresholds: Partial): void { - this.thresholds = { ...this.thresholds, ...thresholds }; - } - - /** 获取所有池的统计信息 */ - getStats(): MemoryStats[] { - const smartManager = globalPoolCoordinator.getSmartManager(); - const allMetrics = smartManager.getAllMetrics(); - const stats: MemoryStats[] = []; - const now = Date.now(); - - allMetrics.forEach((metrics, poolName) => { - const totalAccess = metrics.hitCount + metrics.missCount; - const hitRate = totalAccess > 0 ? (metrics.hitCount / totalAccess) * 100 : 0; - - // 计算创建频率 - let createFrequency = 0; - if (metrics.createTimes.length >= 2) { - const timeSpan = metrics.createTimes[metrics.createTimes.length - 1] - - metrics.createTimes[0]; - if (timeSpan > 0) { - createFrequency = (metrics.createTimes.length / timeSpan) * 1000; - } - } - - // 计算访问频率 - let accessFrequency = 0; - if (metrics.accessTimes && metrics.accessTimes.length >= 2) { - const timeSpan = metrics.accessTimes[metrics.accessTimes.length - 1] - - metrics.accessTimes[0]; - if (timeSpan > 0) { - accessFrequency = (metrics.accessTimes.length / timeSpan) * 1000; - } - } - - // 检测异常 - const issue = this.detectIssue(poolName, metrics, hitRate, now); - - const stat: MemoryStats = { - poolName, - currentSize: metrics.currentSize, - maxSize: metrics.maxSize, - minSize: metrics.minSize, - hitRate, - createCount: metrics.createCount, - recycleCount: metrics.recycleCount, - createFrequency, - accessFrequency, - estimatedMemory: this.formatBytes(metrics.totalMemory), - memoryBytes: metrics.totalMemory, - hasIssue: issue !== null, - issueType: issue || undefined - }; - - stats.push(stat); - this.lastStats.set(poolName, stat); - }); - - return stats; - } - - /** 检测池的异常情况 */ - private detectIssue(poolName: string, metrics: any, hitRate: number, now: number): - 'low-hit-rate' | 'memory-leak' | 'over-capacity' | 'unused' | null { - - // 检测低命中率 - if (metrics.hitCount + metrics.missCount > 20 && hitRate < this.thresholds.lowHitRate) { - return 'low-hit-rate'; - } - - // 检测容量过高 - const capacityUsage = metrics.maxSize > 0 ? (metrics.currentSize / metrics.maxSize) * 100 : 0; - if (capacityUsage > this.thresholds.highCapacity) { - return 'over-capacity'; - } - - // 检测长期未使用 - if (now - metrics.lastAccessTime > this.thresholds.unusedTime) { - return 'unused'; - } - - // 检测内存泄漏(与上次对比) - const lastStat = this.lastStats.get(poolName); - if (lastStat && lastStat.memoryBytes > 0) { - const growthRate = (metrics.totalMemory / lastStat.memoryBytes) * 100; - if (growthRate > this.thresholds.memoryLeakGrowth && metrics.totalMemory > 1024 * 1024) { - return 'memory-leak'; - } - } - - return null; - } - - /** 格式化字节数 */ - private formatBytes(bytes: number): string { - if (bytes === 0) return '0 B'; - const k = 1024; - const sizes = ['B', 'KB', 'MB', 'GB']; - const i = Math.floor(Math.log(bytes) / Math.log(k)); - return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; - } - - /** - * 统一的日志输出方法 - * @param config 监控配置 - */ - log(config: MonitorConfig = {}): void { - const { - level = 'normal', - showIssues = true, - showMemory = true, - sortBy = 'frequency' - } = config; - - const stats = this.getStats(); - - if (stats.length === 0) { - console.log('[ECS Pool] 暂无对象池数据'); - return; - } - - // 排序 - this.sortStats(stats, sortBy); - - // 根据详细程度输出 - switch (level) { - case 'compact': - this.logCompact(stats, showMemory); - break; - case 'detailed': - this.logDetailed(stats, showIssues, showMemory); - break; - default: - this.logNormal(stats, showIssues, showMemory); - } - } - - /** 简洁输出 */ - private logCompact(stats: MemoryStats[], showMemory: boolean): void { - const totalCurrent = stats.reduce((sum, s) => sum + s.currentSize, 0); - const avgHitRate = stats.reduce((sum, s) => sum + s.hitRate, 0) / stats.length; - const totalMemory = stats.reduce((sum, s) => sum + s.memoryBytes, 0); - const issueCount = stats.filter(s => s.hasIssue).length; - - let output = `[ECS Pool] 池数:${stats.length} | 缓存:${totalCurrent} | 命中率:${avgHitRate.toFixed(1)}%`; - if (showMemory) { - output += ` | 内存:${this.formatBytes(totalMemory)}`; - } - if (issueCount > 0) { - output += ` | ⚠️异常:${issueCount}`; - } - console.log(output); - } - - /** 普通输出 */ - private logNormal(stats: MemoryStats[], showIssues: boolean, showMemory: boolean): void { - console.log('\n========== ECS 对象池统计 =========='); - console.log(`总池数量: ${stats.length}`); - - if (showMemory) { - const totalMemory = stats.reduce((sum, s) => sum + s.memoryBytes, 0); - const memoryUsage = globalPoolCoordinator.getMemoryUsage(); - console.log(`总内存: ${this.formatBytes(totalMemory)} / ${this.formatBytes(memoryUsage.budget)} (${memoryUsage.percent.toFixed(1)}%)`); - } - - if (showIssues) { - const issueStats = stats.filter(s => s.hasIssue); - if (issueStats.length > 0) { - console.log(`⚠️ 异常池: ${issueStats.length}`); - } - } - console.log(''); - - stats.forEach(stat => { - const icon = this.getStatusIcon(stat); - console.log(`${icon} 【${stat.poolName}】`); - console.log(` 容量: ${stat.currentSize}/${stat.maxSize} (最小:${stat.minSize})`); - console.log(` 命中率: ${stat.hitRate.toFixed(1)}% ${this.getHitRateDesc(stat.hitRate)}`); - console.log(` 创建/回收: ${stat.createCount}/${stat.recycleCount}`); - console.log(` 频率: 创建${stat.createFrequency.toFixed(2)}/s, 访问${stat.accessFrequency.toFixed(2)}/s`); - - if (showMemory) { - console.log(` 内存: ${stat.estimatedMemory}`); - } - - if (showIssues && stat.hasIssue) { - console.log(` ⚠️ 异常: ${this.getIssueDesc(stat.issueType!)}`); - } - console.log(''); - }); - - console.log('====================================\n'); - } - - /** 详细输出 */ - private logDetailed(stats: MemoryStats[], showIssues: boolean, showMemory: boolean): void { - console.log('ECS 对象池详细统计报告'); - - // 总览 - const totalCreate = stats.reduce((sum, s) => sum + s.createCount, 0); - const totalRecycle = stats.reduce((sum, s) => sum + s.recycleCount, 0); - const totalCurrent = stats.reduce((sum, s) => sum + s.currentSize, 0); - const avgHitRate = stats.reduce((sum, s) => sum + s.hitRate, 0) / stats.length; - const totalMemory = stats.reduce((sum, s) => sum + s.memoryBytes, 0); - - console.log('📊 总体概况'); - console.log('─────────────────────────────────────────────────────────'); - console.log(` 对象池数量: ${stats.length}`); - console.log(` 总创建次数: ${totalCreate}`); - console.log(` 总回收次数: ${totalRecycle}`); - console.log(` 当前缓存数: ${totalCurrent}`); - console.log(` 平均命中率: ${avgHitRate.toFixed(1)}%`); - - if (showMemory) { - const memoryUsage = globalPoolCoordinator.getMemoryUsage(); - console.log(` 总内存占用: ${this.formatBytes(totalMemory)}`); - console.log(` 内存预算: ${this.formatBytes(memoryUsage.budget)}`); - console.log(` 内存使用率: ${memoryUsage.percent.toFixed(1)}%`); - } - console.log(''); - - // 异常池 - if (showIssues) { - const issueStats = stats.filter(s => s.hasIssue); - if (issueStats.length > 0) { - console.log('⚠️ 异常池列表'); - console.log('─────────────────────────────────────────────────────────'); - issueStats.forEach(stat => { - console.log(` ${stat.poolName}: ${this.getIssueDesc(stat.issueType!)}`); - }); - console.log(''); - } - } - - console.log('📈 各池详细数据'); - console.log('─────────────────────────────────────────────────────────\n'); - - stats.forEach((stat, index) => { - const usage = stat.maxSize > 0 ? (stat.currentSize / stat.maxSize * 100).toFixed(1) : '0'; - const icon = this.getStatusIcon(stat); - - console.log(`${index + 1}. ${stat.poolName} ${icon}`); - console.log(` ├─ 容量: ${stat.currentSize}/${stat.maxSize} (${usage}% 使用率)`); - console.log(` ├─ 命中率: ${stat.hitRate.toFixed(1)}% ${this.getHitRateDesc(stat.hitRate)}`); - console.log(` ├─ 创建/回收: ${stat.createCount} / ${stat.recycleCount}`); - console.log(` ├─ 创建频率: ${stat.createFrequency.toFixed(2)} 个/秒`); - console.log(` ├─ 访问频率: ${stat.accessFrequency.toFixed(2)} 个/秒`); - - if (showMemory) { - console.log(` ├─ 估算内存: ${stat.estimatedMemory}`); - } - - if (showIssues && stat.hasIssue) { - console.log(` └─ ⚠️ 异常: ${this.getIssueDesc(stat.issueType!)}`); - } else { - console.log(` └─ 状态: 正常`); - } - console.log(''); - }); - - console.log('═════════════════════════════════════════════════════════\n'); - } - - /** 排序统计数据 */ - private sortStats(stats: MemoryStats[], sortBy: string): void { - switch (sortBy) { - case 'frequency': - stats.sort((a, b) => b.createFrequency - a.createFrequency); - break; - case 'hitRate': - stats.sort((a, b) => b.hitRate - a.hitRate); - break; - case 'memory': - stats.sort((a, b) => b.memoryBytes - a.memoryBytes); - break; - case 'name': - stats.sort((a, b) => a.poolName.localeCompare(b.poolName)); - break; - } - } - - /** 获取状态图标 */ - private getStatusIcon(stat: MemoryStats): string { - if (stat.hasIssue) { - switch (stat.issueType) { - case 'low-hit-rate': return '🔴'; - case 'memory-leak': return '💥'; - case 'over-capacity': return '⚠️'; - case 'unused': return '💤'; - } - } - return stat.hitRate >= 80 ? '🟢' : stat.hitRate >= 50 ? '🟡' : '🔴'; - } - - /** 获取异常描述 */ - private getIssueDesc(issueType: string): string { - switch (issueType) { - case 'low-hit-rate': return '命中率过低'; - case 'memory-leak': return '疑似内存泄漏'; - case 'over-capacity': return '容量使用率过高'; - case 'unused': return '长期未使用'; - default: return '未知异常'; - } - } - - /** 打印指定池的统计 */ - logPool(poolName: string): void { - const stats = this.getStats(); - const stat = stats.find(s => s.poolName === poolName); - - if (!stat) { - console.log(`[ECS Pool] 未找到池: ${poolName}`); - return; - } - - const usage = stat.maxSize > 0 ? (stat.currentSize / stat.maxSize * 100).toFixed(1) : '0'; - const icon = this.getStatusIcon(stat); - - console.log(`\n【${stat.poolName}】${icon}`); - console.log(` 容量使用: ${stat.currentSize}/${stat.maxSize} (${usage}%)`); - console.log(` 命中率: ${stat.hitRate.toFixed(1)}% ${this.getHitRateDesc(stat.hitRate)}`); - console.log(` 创建次数: ${stat.createCount}`); - console.log(` 回收次数: ${stat.recycleCount}`); - console.log(` 创建频率: ${stat.createFrequency.toFixed(2)} 个/秒`); - console.log(` 访问频率: ${stat.accessFrequency.toFixed(2)} 个/秒`); - console.log(` 估算内存: ${stat.estimatedMemory}`); - - if (stat.hasIssue) { - console.log(` ⚠️ 异常: ${this.getIssueDesc(stat.issueType!)}`); - } - console.log(''); - } - - /** 获取命中率描述 */ - private getHitRateDesc(hitRate: number): string { - if (hitRate >= 90) return '(优秀)'; - if (hitRate >= 70) return '(良好)'; - if (hitRate >= 50) return '(一般)'; - return '(较差)'; - } - - /** 开始监控(定时打印) */ - startMonitoring(config: MonitorConfig & { interval?: number } = {}): void { - if (this.isMonitoring) { - console.warn('[Monitor] 监控已在运行中'); - return; - } - - const { interval = 5000, ...monitorConfig } = config; - this.updateInterval = interval; - this.isMonitoring = true; - - console.log(`[Monitor] 开始监控,更新间隔: ${interval}ms`); - - this.timerId = setInterval(() => { - this.log(monitorConfig); - }, interval); - } - - /** 停止监控 */ - stopMonitoring(): void { - if (!this.isMonitoring) { - return; - } - - if (this.timerId) { - clearInterval(this.timerId); - this.timerId = null; - } - - this.isMonitoring = false; - console.log('[Monitor] 监控已停止'); - } - - /** 获取异常池列表 */ - getIssues(): MemoryStats[] { - return this.getStats().filter(s => s.hasIssue); - } - - /** 获取类型分析报告 */ - getTypeProfiles() { - return globalPoolCoordinator.getSmartManager().getTypeProfiles(); - } - - /** 触发内存清理 */ - cleanup(maxAge: number = 60000): number { - return globalPoolCoordinator.getSmartManager().cleanupAllStale(maxAge); - } -} - -/** 导出单例 */ -export const ecsMonitor = ECSMemoryMonitor.getInstance(); - diff --git a/assets/libs/ecs/ECSMemoryMonitor.ts.meta b/assets/libs/ecs/ECSMemoryMonitor.ts.meta deleted file mode 100644 index c67731a..0000000 --- a/assets/libs/ecs/ECSMemoryMonitor.ts.meta +++ /dev/null @@ -1,11 +0,0 @@ -{ - "ver": "4.0.24", - "importer": "typescript", - "imported": true, - "uuid": "c1cce79d-eaea-4205-b53c-982ee27bda62", - "files": [], - "subMetas": {}, - "userData": { - "simulateGlobals": [] - } -} diff --git a/assets/libs/ecs/ECSPoolManager.ts b/assets/libs/ecs/ECSPoolManager.ts index 452f13f..652c13a 100644 --- a/assets/libs/ecs/ECSPoolManager.ts +++ b/assets/libs/ecs/ECSPoolManager.ts @@ -1,549 +1,162 @@ /* - * 动态对象池管理系统 - * 三阶段实现:基础动态池 + 智能特性 + 可选配置 + * 对象池管理系统 */ //#region 类型定义 /** 池统计指标 */ interface PoolMetrics { - // 使用统计 - createCount: number; // 创建次数 - recycleCount: number; // 回收次数 - hitCount: number; // 命中次数(从池中获取) - missCount: number; // 未命中次数(需要新建) - - // 容量管理 - currentSize: number; // 当前池大小 - maxSize: number; // 最大容量 - minSize: number; // 最小容量 - - // 时间统计 - lastAccessTime: number; // 最后访问时间 - lastAdjustTime: number; // 最后调整时间 - createTimes: number[]; // 创建时间戳列表(用于计算频率) - - // 内存估算 - estimatedItemSize: number; // 单个对象估算大小(字节) - totalMemory: number; // 总内存占用 - - // LRU支持 - accessTimes: number[]; // 访问时间戳(用于LRU) -} - -/** 对象类型特征 */ -interface TypeProfile { - typeName: string; - category: 'long-lived' | 'high-frequency' | 'normal' | 'rare'; - peakRate: number; // 峰值创建速率(个/秒) - reuseRate: number; // 复用率 - avgMemorySize: number; // 平均内存大小 - shouldPool: boolean; // 是否应该池化 -} - -/** 池配置 */ -export interface PoolConfig { - enabled?: boolean; // 是否启用池化 - minSize?: number; // 最小容量 - maxSize?: number; // 最大容量 - strategy?: 'dynamic' | 'fixed' | 'lru'; // 策略 - preWarmSize?: number; // 预热数量 -} - -/** 池提示(性能优化) */ -export interface PoolHint { - expectedPeak?: number; // 预期峰值 - sceneAware?: string; // 场景感知标识 - highFrequency?: boolean; // 是否高频对象 -} - -/** 历史数据 */ -interface HistoryData { - typeName: string; - peakCount: number; - avgCount: number; - lastPlayTime: number; + createCount: number; // 创建次数 + recycleCount: number; // 回收次数 + hitCount: number; // 命中次数(从池中获取) + missCount: number; // 未命中次数(需要新建) + currentSize: number; // 当前池大小 } //#endregion -//#region 第一阶段:基础动态池 +//#region 动态对象池 /** * 动态对象池 - * 自动统计、自动调整容量、支持LRU策略 */ class DynamicPool { private pool: T[] = []; private metrics: PoolMetrics; - private config: PoolConfig; - private lruTimestamps: Map = new Map(); // LRU时间戳 - + + /** + * 构造函数 + * @param typeName 池类型名称 + * @param factory 对象工厂函数 + */ constructor( private typeName: string, - private factory: () => T, - config: Partial = {} + private factory: () => T ) { - this.config = { - enabled: true, - minSize: 10, - maxSize: 1000, - strategy: 'dynamic', - preWarmSize: 0, - ...config - }; - this.metrics = { createCount: 0, recycleCount: 0, hitCount: 0, missCount: 0, - currentSize: 0, - maxSize: this.config.maxSize!, - minSize: this.config.minSize!, - lastAccessTime: Date.now(), - lastAdjustTime: Date.now(), - createTimes: [], - estimatedItemSize: 0, - totalMemory: 0, - accessTimes: [] + currentSize: 0 }; - - // 预热 - if (this.config.preWarmSize! > 0) { - this.preWarm(this.config.preWarmSize!); - } - - // 估算对象大小 - this.estimateItemSize(); } - - /** 估算单个对象的内存大小 */ - private estimateItemSize(): void { - if (this.metrics.estimatedItemSize > 0) return; - - try { - const sample = this.factory(); - this.metrics.estimatedItemSize = this.calculateObjectSize(sample); - this.updateTotalMemory(); - } catch (e) { - console.warn(`[Pool] ${this.typeName} 无法估算对象大小`, e); - this.metrics.estimatedItemSize = 1024; // 默认1KB - } - } - - /** 计算对象大小(粗略估算) */ - private calculateObjectSize(obj: any): number { - let size = 0; - const seen = new WeakSet(); - - const calculate = (o: any): void => { - if (o === null || o === undefined) return; - if (typeof o !== 'object') { - size += 8; // 基本类型约8字节 - return; - } - if (seen.has(o)) return; - seen.add(o); - - // 对象本身的开销 - size += 32; - - // 遍历属性 - for (const key in o) { - if (o.hasOwnProperty(key)) { - size += key.length * 2; // 键名 - const value = o[key]; - if (typeof value === 'string') { - size += value.length * 2; - } else if (typeof value === 'number' || typeof value === 'boolean') { - size += 8; - } else if (typeof value === 'object' && value !== null) { - calculate(value); - } - } - } - }; - - calculate(obj); - return size; - } - - /** 更新总内存占用 */ - private updateTotalMemory(): void { - this.metrics.totalMemory = this.metrics.currentSize * this.metrics.estimatedItemSize; - } - - /** 获取对象 */ + + /** + * 从池中获取对象 + * @returns 池中的对象或新创建的对象 + */ get(): T { - const now = Date.now(); - this.metrics.lastAccessTime = now; - this.metrics.accessTimes.push(now); - - // 只保留最近100次访问时间 - if (this.metrics.accessTimes.length > 100) { - this.metrics.accessTimes.shift(); - } - if (this.pool.length > 0) { this.metrics.hitCount++; const item = this.pool.pop()!; - - // LRU: 记录使用时间 - if (this.config.strategy === 'lru') { - this.lruTimestamps.set(item, now); - } - this.metrics.currentSize = this.pool.length; - this.updateTotalMemory(); return item; } - - // 池中没有,创建新对象 + this.metrics.missCount++; this.metrics.createCount++; - this.metrics.createTimes.push(now); - - // 只保留最近100次创建时间 - if (this.metrics.createTimes.length > 100) { - this.metrics.createTimes.shift(); - } - - const item = this.factory(); - - // LRU: 记录创建时间 - if (this.config.strategy === 'lru') { - this.lruTimestamps.set(item, now); - } - - // 触发自动调整 - this.autoAdjust(); - - return item; + + return this.factory(); } - - /** 回收对象 */ + + /** + * 回收对象到池中 + * @param item 要回收的对象 + */ recycle(item: T): void { - if (!this.config.enabled) { - return; - } - - this.metrics.recycleCount++; - - // 检查是否超过最大容量 - if (this.pool.length < this.metrics.maxSize) { - this.pool.push(item); - this.metrics.currentSize = this.pool.length; - this.updateTotalMemory(); - } else if (this.config.strategy === 'lru') { - // LRU策略:移除最久未使用的对象 - this.evictLRU(); - this.pool.push(item); - this.metrics.currentSize = this.pool.length; - this.updateTotalMemory(); - } - - // 触发自动调整 - this.autoAdjust(); + this.pool.push(item); + this.metrics.currentSize = this.pool.length; } - - /** LRU淘汰策略 */ - private evictLRU(): void { - if (this.pool.length === 0) return; - - let oldestItem: T | null = null; - let oldestTime = Date.now(); - - // 找到最久未使用的对象 - for (const item of this.pool) { - const time = this.lruTimestamps.get(item) || 0; - if (time < oldestTime) { - oldestTime = time; - oldestItem = item; - } - } - - // 移除最久未使用的对象 - if (oldestItem) { - const index = this.pool.indexOf(oldestItem); - if (index > -1) { - this.pool.splice(index, 1); - this.lruTimestamps.delete(oldestItem); - } - } - } - - /** 自动调整池容量 */ - private autoAdjust(): void { - const now = Date.now(); - const timeSinceLastAdjust = now - this.metrics.lastAdjustTime; - - // 每5秒调整一次 - if (timeSinceLastAdjust < 5000) { - return; - } - - this.metrics.lastAdjustTime = now; - - if (this.config.strategy === 'fixed') { - return; // 固定策略不调整 - } - - // 计算命中率 - const totalAccess = this.metrics.hitCount + this.metrics.missCount; - if (totalAccess < 10) { - return; // 样本太少,不调整 - } - - const hitRate = this.metrics.hitCount / totalAccess; - - // 根据命中率调整最大容量 - if (hitRate < 0.5) { - // 命中率低,需要扩容 - const newMax = Math.min( - Math.floor(this.metrics.maxSize * 1.5), - this.config.maxSize! - ); - if (newMax > this.metrics.maxSize) { - console.log(`[Pool] ${this.typeName} 扩容: ${this.metrics.maxSize} -> ${newMax} (命中率: ${(hitRate * 100).toFixed(1)}%)`); - this.metrics.maxSize = newMax; - } - } else if (hitRate > 0.9 && this.pool.length > this.metrics.minSize * 2) { - // 命中率高且池很大,可以缩容 - const newMax = Math.max( - Math.floor(this.metrics.maxSize * 0.8), - this.metrics.minSize - ); - if (newMax < this.metrics.maxSize) { - console.log(`[Pool] ${this.typeName} 缩容: ${this.metrics.maxSize} -> ${newMax} (命中率: ${(hitRate * 100).toFixed(1)}%)`); - this.metrics.maxSize = newMax; - - // 清理多余对象 - while (this.pool.length > newMax) { - this.pool.pop(); - } - this.metrics.currentSize = this.pool.length; - } - } - } - - /** 预热池 */ + + /** + * 预热池,提前创建指定数量的对象 + * @param count 要预热的对象数量 + */ preWarm(count: number): void { - const targetCount = Math.min(count, this.metrics.maxSize); - while (this.pool.length < targetCount) { + while (this.pool.length < count) { this.pool.push(this.factory()); this.metrics.createCount++; } this.metrics.currentSize = this.pool.length; - console.log(`[Pool] ${this.typeName} 预热完成: ${this.pool.length} 个对象`); } - - /** 获取统计信息 */ + + /** + * 获取池的统计信息 + * @returns 只读的统计指标对象 + */ getMetrics(): Readonly { return { ...this.metrics }; } - - /** 清空池 */ + + /** + * 清空池中的所有对象 + */ clear(): void { this.pool.length = 0; - this.lruTimestamps.clear(); this.metrics.currentSize = 0; - this.updateTotalMemory(); } - - /** 清理长期未使用的对象(用于内存压力下的主动清理) */ - cleanupStale(maxAge: number = 60000): number { - if (this.config.strategy !== 'lru') return 0; - - const now = Date.now(); - let cleaned = 0; - - // 保留最小容量 - const targetSize = Math.max(this.metrics.minSize, Math.floor(this.pool.length * 0.5)); - - for (let i = this.pool.length - 1; i >= 0 && this.pool.length > targetSize; i--) { - const item = this.pool[i]; - const lastUse = this.lruTimestamps.get(item) || 0; - - if (now - lastUse > maxAge) { - this.pool.splice(i, 1); - this.lruTimestamps.delete(item); - cleaned++; - } + + /** + * 手动缩减池大小到指定容量 + * @param targetSize 目标池大小 + * @returns 移除的对象数量 + */ + shrinkTo(targetSize: number): number { + let removed = 0; + + while (this.pool.length > targetSize) { + this.pool.pop(); + removed++; } - - if (cleaned > 0) { + + if (removed > 0) { this.metrics.currentSize = this.pool.length; - this.updateTotalMemory(); - console.log(`[Pool] ${this.typeName} 清理了 ${cleaned} 个过期对象`); } - - return cleaned; - } - - /** 计算创建频率(个/秒) */ - getCreateFrequency(): number { - if (this.metrics.createTimes.length < 2) { - return 0; - } - - const timeSpan = this.metrics.createTimes[this.metrics.createTimes.length - 1] - - this.metrics.createTimes[0]; - if (timeSpan === 0) { - return 0; - } - - return (this.metrics.createTimes.length / timeSpan) * 1000; - } - - /** 计算访问频率(个/秒) */ - getAccessFrequency(): number { - if (this.metrics.accessTimes.length < 2) { - return 0; - } - - const timeSpan = this.metrics.accessTimes[this.metrics.accessTimes.length - 1] - - this.metrics.accessTimes[0]; - if (timeSpan === 0) { - return 0; - } - - return (this.metrics.accessTimes.length / timeSpan) * 1000; + + return removed; } } //#endregion -//#region 第二阶段:智能特性 +//#region 池管理器 /** - * 智能池管理器 - * 添加学习、预热、类型分析功能 + * 池管理器 */ -class SmartPoolManager { +class PoolManager { private pools: Map> = new Map(); - private typeProfiles: Map = new Map(); - private historyData: Map = new Map(); - private learningEnabled = true; - private readonly HISTORY_KEY = 'ecs_pool_history'; - - constructor() { - this.loadHistory(); - } - - /** 获取或创建池 */ - getPool(typeName: string, factory: () => T, config?: Partial): DynamicPool { + + /** + * 获取或创建池 + * @param typeName 池类型名称 + * @param factory 对象工厂函数 + * @returns 动态对象池实例 + */ + getPool(typeName: string, factory: () => T): DynamicPool { if (!this.pools.has(typeName)) { - // 从历史数据学习 - const history = this.historyData.get(typeName); - const learnedConfig = this.learnFromHistory(typeName, history); - - // 合并配置 - const finalConfig = { ...learnedConfig, ...config }; - - const pool = new DynamicPool(typeName, factory, finalConfig); + const pool = new DynamicPool(typeName, factory); this.pools.set(typeName, pool); - - console.log(`[SmartPool] 创建池: ${typeName}`, finalConfig); } - + return this.pools.get(typeName)!; } - - /** 从历史数据学习 */ - private learnFromHistory(typeName: string, history?: HistoryData): Partial { - if (!history || !this.learningEnabled) { - return {}; - } - - const config: Partial = {}; - - // 根据历史峰值设置预热和最大容量 - if (history.peakCount > 0) { - config.preWarmSize = Math.floor(history.avgCount * 0.5); - config.maxSize = Math.floor(history.peakCount * 1.2); - config.minSize = Math.floor(history.avgCount * 0.2); - - console.log(`[SmartPool] ${typeName} 从历史学习: 峰值=${history.peakCount}, 平均=${history.avgCount}`); - } - - return config; + + /** + * 清空所有池 + */ + clearAll(): void { + this.pools.forEach(pool => pool.clear()); + this.pools.clear(); } - - /** 分析类型特征 */ - analyzeType(typeName: string): TypeProfile { - const pool = this.pools.get(typeName); - if (!pool) { - return this.createDefaultProfile(typeName); - } - - const metrics = pool.getMetrics(); - const frequency = pool.getCreateFrequency(); - - // 计算复用率 - const reuseRate = metrics.recycleCount > 0 - ? metrics.hitCount / metrics.recycleCount - : 0; - - // 分类 - let category: TypeProfile['category'] = 'normal'; - if (frequency > 50) { - category = 'high-frequency'; - } else if (reuseRate < 0.2) { - category = 'rare'; - } else if (metrics.createCount < 10) { - category = 'long-lived'; - } - - const profile: TypeProfile = { - typeName, - category, - peakRate: frequency, - reuseRate, - avgMemorySize: metrics.estimatedItemSize, - shouldPool: this.shouldPool(category, reuseRate, frequency) - }; - - this.typeProfiles.set(typeName, profile); - return profile; - } - - /** 判断是否应该池化 */ - private shouldPool(category: string, reuseRate: number, frequency: number): boolean { - // 高频对象必须池化 - if (category === 'high-frequency') { - return true; - } - - // 复用率太低不池化 - if (reuseRate < 0.1) { - return false; - } - - // 长生命周期且数量少不池化 - if (category === 'long-lived' && frequency < 1) { - return false; - } - - return true; - } - - /** 创建默认特征 */ - private createDefaultProfile(typeName: string): TypeProfile { - return { - typeName, - category: 'normal', - peakRate: 0, - reuseRate: 0, - avgMemorySize: 0, - shouldPool: true - }; - } - - /** 场景感知预热 */ + + /** + * 场景切换时预热池 + * @param sceneType 场景类型标识 + * @param hints 类型名称到预热数量的映射 + */ onSceneChange(sceneType: string, hints: Map): void { - console.log(`[SmartPool] 场景切换: ${sceneType}`); - hints.forEach((count, typeName) => { const pool = this.pools.get(typeName); if (pool) { @@ -551,46 +164,11 @@ class SmartPoolManager { } }); } - - /** 保存历史数据 */ - saveHistory(): void { - const history: Record = {}; - - this.pools.forEach((pool, typeName) => { - const metrics = pool.getMetrics(); - history[typeName] = { - typeName, - peakCount: metrics.maxSize, - avgCount: Math.floor((metrics.hitCount + metrics.missCount) / 2), - lastPlayTime: Date.now() - }; - }); - - try { - localStorage.setItem(this.HISTORY_KEY, JSON.stringify(history)); - console.log('[SmartPool] 历史数据已保存'); - } catch (e) { - console.warn('[SmartPool] 保存历史数据失败', e); - } - } - - /** 加载历史数据 */ - private loadHistory(): void { - try { - const data = localStorage.getItem(this.HISTORY_KEY); - if (data) { - const history = JSON.parse(data); - Object.entries(history).forEach(([typeName, data]) => { - this.historyData.set(typeName, data as HistoryData); - }); - console.log(`[SmartPool] 加载历史数据: ${this.historyData.size} 个类型`); - } - } catch (e) { - console.warn('[SmartPool] 加载历史数据失败', e); - } - } - - /** 获取所有池的统计信息 */ + + /** + * 获取所有池的统计信息 + * @returns 类型名称到统计指标的映射 + */ getAllMetrics(): Map { const result = new Map(); this.pools.forEach((pool, typeName) => { @@ -598,74 +176,30 @@ class SmartPoolManager { }); return result; } - - /** 清空所有池 */ - clearAll(): void { - this.pools.forEach(pool => pool.clear()); - console.log('[SmartPool] 所有池已清空'); - } - - /** 清理所有池中的过期对象 */ - cleanupAllStale(maxAge: number = 60000): number { - let totalCleaned = 0; + + /** + * 手动缩减所有池到指定百分比 + * @param percent 目标百分比(0-1之间) + * @returns 总共移除的对象数量 + */ + shrinkAllTo(percent: number): number { + let totalRemoved = 0; this.pools.forEach(pool => { - totalCleaned += pool.cleanupStale(maxAge); + const metrics = pool.getMetrics(); + const targetSize = Math.floor(metrics.currentSize * percent); + totalRemoved += pool.shrinkTo(targetSize); }); - if (totalCleaned > 0) { - console.log(`[SmartPool] 总共清理了 ${totalCleaned} 个过期对象`); - } - return totalCleaned; + return totalRemoved; } - - /** 获取总内存占用 */ - getTotalMemory(): number { - let total = 0; - this.pools.forEach(pool => { - total += pool.getMetrics().totalMemory; - }); - return total; + + /** + * 获取指定类型的池 + * @param typeName 池类型名称 + * @returns 池实例,如果不存在则返回undefined + */ + getPoolByName(typeName: string): DynamicPool | undefined { + return this.pools.get(typeName); } - - /** 获取类型分析报告 */ - getTypeProfiles(): Map { - // 更新所有类型的分析 - this.pools.forEach((pool, typeName) => { - this.analyzeType(typeName); - }); - return new Map(this.typeProfiles); - } -} - -//#endregion - -//#region 第三阶段:可选配置 - -/** 池配置装饰器 */ -export function poolConfig(config: PoolConfig) { - return function (constructor: T) { - // 将配置存储到构造函数上 - (constructor as any).__poolConfig = config; - return constructor; - }; -} - -/** 池提示装饰器 */ -export function poolHint(hint: PoolHint) { - return function (constructor: T) { - // 将提示存储到构造函数上 - (constructor as any).__poolHint = hint; - return constructor; - }; -} - -/** 获取类的池配置 */ -export function getPoolConfig(constructor: any): PoolConfig | undefined { - return constructor.__poolConfig; -} - -/** 获取类的池提示 */ -export function getPoolHint(constructor: any): PoolHint | undefined { - return constructor.__poolHint; } //#endregion @@ -673,136 +207,113 @@ export function getPoolHint(constructor: any): PoolHint | undefined { //#region 全局协调器 /** - * 全局池协调器 - * 管理总内存预算,协调各池之间的资源分配 + * 全局池协调器 - 提供手动管理API */ class GlobalPoolCoordinator { - private smartManager: SmartPoolManager; - private totalBudget = 100 * 1024 * 1024; // 100MB默认预算 - private cleanupTimer: any = null; - private autoCleanupEnabled = false; - + private poolManager: PoolManager; + constructor() { - this.smartManager = new SmartPoolManager(); + this.poolManager = new PoolManager(); } - - /** 获取或创建池 */ - getPool(typeName: string, factory: () => T, ctor?: any): DynamicPool { - let config: Partial = {}; - let hint: PoolHint | undefined; - - // 读取装饰器配置 - if (ctor) { - const decoratorConfig = getPoolConfig(ctor); - if (decoratorConfig) { - config = decoratorConfig; - } - - hint = getPoolHint(ctor); - if (hint) { - // 应用提示 - if (hint.expectedPeak) { - config.maxSize = hint.expectedPeak; - config.preWarmSize = Math.floor(hint.expectedPeak * 0.3); - } - if (hint.highFrequency) { - config.minSize = 50; - config.maxSize = config.maxSize || 1000; - } - } - } - - return this.smartManager.getPool(typeName, factory, config); + + /** + * 获取或创建池 + * @param typeName 池类型名称 + * @param factory 对象工厂函数 + * @returns 动态对象池实例 + */ + getPool(typeName: string, factory: () => T): DynamicPool { + return this.poolManager.getPool(typeName, factory); } - - /** 场景切换 */ + + /** + * 获取池管理器实例 + * @returns 池管理器 + */ + getPoolManager(): PoolManager { + return this.poolManager; + } + + /** + * 场景切换时预热池 + * @param sceneType 场景类型标识 + * @param hints 类型名称到预热数量的映射 + */ onSceneChange(sceneType: string, hints: Map): void { - this.smartManager.onSceneChange(sceneType, hints); + this.poolManager.onSceneChange(sceneType, hints); } - - /** 获取智能管理器 */ - getSmartManager(): SmartPoolManager { - return this.smartManager; + + /** + * 手动缩减所有池到指定百分比 + * @param percent 目标百分比(0-1之间) + * @returns 总共移除的对象数量 + */ + shrinkAll(percent: number): number { + return this.poolManager.shrinkAllTo(percent); } - - /** 设置总内存预算 */ - setTotalBudget(bytes: number): void { - this.totalBudget = bytes; - console.log(`[GlobalCoordinator] 设置内存预算: ${(bytes / 1024 / 1024).toFixed(2)}MB`); - - // 检查是否超出预算 - this.checkMemoryBudget(); + + /** + * 清空所有池中的对象 + */ + clearAll(): void { + this.poolManager.clearAll(); } - - /** 检查内存预算 */ - private checkMemoryBudget(): void { - const usedMemory = this.smartManager.getTotalMemory(); - const usagePercent = (usedMemory / this.totalBudget) * 100; - - if (usedMemory > this.totalBudget) { - console.warn(`[GlobalCoordinator] 内存超出预算! 使用: ${(usedMemory / 1024 / 1024).toFixed(2)}MB / ${(this.totalBudget / 1024 / 1024).toFixed(2)}MB (${usagePercent.toFixed(1)}%)`); - - // 触发清理 - this.smartManager.cleanupAllStale(30000); // 清理30秒未使用的对象 - - // 再次检查 - const newUsed = this.smartManager.getTotalMemory(); - if (newUsed > this.totalBudget) { - console.warn(`[GlobalCoordinator] 清理后仍超出预算,考虑增加预算或优化对象使用`); - } - } else if (usagePercent > 80) { - console.warn(`[GlobalCoordinator] 内存使用率较高: ${usagePercent.toFixed(1)}%`); + + /** + * 清空指定池中的对象 + * @param typeName 池类型名称 + */ + clearPool(typeName: string): void { + const pool = this.poolManager.getPoolByName(typeName); + if (pool) { + pool.clear(); } } - - /** 获取当前内存使用情况 */ - getMemoryUsage(): { used: number; budget: number; percent: number } { - const used = this.smartManager.getTotalMemory(); - return { - used, - budget: this.totalBudget, - percent: (used / this.totalBudget) * 100 - }; + + /** + * 手动缩减指定池到目标大小 + * @param typeName 池类型名称 + * @param targetSize 目标池大小 + * @returns 移除的对象数量 + */ + shrinkPool(typeName: string, targetSize: number): number { + const pool = this.poolManager.getPoolByName(typeName); + return pool ? pool.shrinkTo(targetSize) : 0; } - - /** 启用自动清理(定期清理过期对象) */ - enableAutoCleanup(interval: number = 60000): void { - if (this.autoCleanupEnabled) { - console.warn('[GlobalCoordinator] 自动清理已启用'); - return; - } - - this.autoCleanupEnabled = true; - this.cleanupTimer = setInterval(() => { - this.smartManager.cleanupAllStale(); - this.checkMemoryBudget(); - }, interval); - - console.log(`[GlobalCoordinator] 启用自动清理,间隔: ${interval}ms`); + + /** + * 获取所有池的统计信息 + * @returns 类型名称到统计指标的映射 + */ + getAllMetrics(): Map { + return this.poolManager.getAllMetrics(); } - - /** 禁用自动清理 */ - disableAutoCleanup(): void { - if (this.cleanupTimer) { - clearInterval(this.cleanupTimer); - this.cleanupTimer = null; - } - this.autoCleanupEnabled = false; - console.log('[GlobalCoordinator] 禁用自动清理'); - } - - /** 保存历史数据 */ - saveHistory(): void { - this.smartManager.saveHistory(); + + /** + * 获取指定池的统计信息 + * @param typeName 池类型名称 + * @returns 统计指标对象,如果池不存在则返回undefined + */ + getPoolMetrics(typeName: string): PoolMetrics | undefined { + const pool = this.poolManager.getPoolByName(typeName); + return pool ? pool.getMetrics() : undefined; } } //#endregion -//#region 导出单例 - -/** 全局池协调器实例 */ -export const globalPoolCoordinator = new GlobalPoolCoordinator(); - -//#endregion - +/** + * 全局池协调器实例 + * + * 用于管理 ECS 框架中的对象池,主要包括: + * 1. ECS 实体对象 (ECSEntity) - 实体销毁后回收复用 + * 2. ECS 组件对象 (IComp) - 组件移除后回收复用 + * 3. 其他自定义对象 - 支持任意类型的对象池化 + * + * 核心功能: + * - 对象复用:减少频繁创建销毁带来的性能开销 + * - 统计监控:跟踪命中率、创建次数等指标 + * - 池管理:支持预热、缩减、清空等操作 + * - 场景优化:场景切换时自动预热相关对象池 + */ +export const ecsPoolCoordinator = new GlobalPoolCoordinator(); \ No newline at end of file