From f3dc8c63441b665fecc47d76c207c987c387564b Mon Sep 17 00:00:00 2001 From: YHH <359807859@qq.com> Date: Wed, 30 Jul 2025 17:10:58 +0800 Subject: [PATCH] =?UTF-8?q?BigIntFactory=20=E7=BC=93=E5=AD=98=E4=BC=98?= =?UTF-8?q?=E5=8C=96=20=20=20-=20=E4=B8=BA=20zero()=20=E5=92=8C=20one()=20?= =?UTF-8?q?=E6=96=B9=E6=B3=95=E6=B7=BB=E5=8A=A0=E7=BC=93=E5=AD=98=EF=BC=8C?= =?UTF-8?q?=E9=81=BF=E5=85=8D=E9=87=8D=E5=A4=8D=E5=88=9B=E5=BB=BA=E5=AF=B9?= =?UTF-8?q?=E8=B1=A1=20ComponentIndexManager=20=E4=BC=98=E5=8C=96=20=20=20?= =?UTF-8?q?-=20=E6=B7=BB=E5=8A=A0=E4=BA=86=E7=A9=BA=E5=AE=9E=E4=BD=93?= =?UTF-8?q?=E6=A3=80=E6=9F=A5=EF=BC=8C=E8=B7=B3=E8=BF=87=E4=B8=8D=E5=BF=85?= =?UTF-8?q?=E8=A6=81=E7=9A=84=E7=B4=A2=E5=BC=95=E6=93=8D=E4=BD=9C=20=20=20?= =?UTF-8?q?-=20=E5=AE=9E=E7=8E=B0=E4=BA=86=20Set=20=E5=AF=B9=E8=B1=A1?= =?UTF-8?q?=E6=B1=A0=EF=BC=8C=E9=87=8D=E7=94=A8=20Set=20=E5=AE=9E=E4=BE=8B?= =?UTF-8?q?=E4=BB=A5=E5=87=8F=E5=B0=91=E5=86=85=E5=AD=98=E5=88=86=E9=85=8D?= =?UTF-8?q?=20=20=20-=20=E4=BC=98=E5=8C=96=E4=BA=86=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=A3=80=E6=9F=A5=E9=A2=91=E7=8E=87=EF=BC=8C?= =?UTF-8?q?=E4=BB=8E=E6=AF=8F=E6=AC=A1=E6=93=8D=E4=BD=9C=E5=8F=98=E4=B8=BA?= =?UTF-8?q?=E6=AF=8F100=E6=AC=A1=E6=93=8D=E4=BD=9C=E6=A3=80=E6=9F=A5?= =?UTF-8?q?=E4=B8=80=E6=AC=A1=20EntityManager=20=E4=BC=98=E5=8C=96=20=20?= =?UTF-8?q?=20-=20=E5=AF=B9=E7=A9=BA=E5=AE=9E=E4=BD=93=E8=B7=B3=E8=BF=87?= =?UTF-8?q?=E4=B8=8D=E5=BF=85=E8=A6=81=E7=9A=84=E7=BB=84=E4=BB=B6=E7=B4=A2?= =?UTF-8?q?=E5=BC=95=E3=80=81=E5=8E=9F=E5=9E=8B=E7=B3=BB=E7=BB=9F=E5=92=8C?= =?UTF-8?q?=E8=84=8F=E6=A0=87=E8=AE=B0=E6=93=8D=E4=BD=9C=20=20=20-=20?= =?UTF-8?q?=E6=89=B9=E9=87=8F=E5=88=9B=E5=BB=BA=E6=97=B6=E5=90=8C=E6=A0=B7?= =?UTF-8?q?=E5=BA=94=E7=94=A8=E7=A9=BA=E5=AE=9E=E4=BD=93=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/ECS/Core/ComponentIndex.ts | 39 ++- src/ECS/Core/EntityManager.ts | 17 +- src/ECS/Utils/BigIntCompatibility.ts | 12 +- tests/ECS/Core/ComponentIndexManager.test.ts | 302 ++++++++++++++++++ ...ledPerformanceAnalysis.performance.test.ts | 227 +++++++++++++ ...itializationComparison.performance.test.ts | 176 ++++++++++ ...zedPerformanceAnalysis.performance.test.ts | 171 ++++++++++ 7 files changed, 929 insertions(+), 15 deletions(-) create mode 100644 tests/ECS/Core/ComponentIndexManager.test.ts create mode 100644 tests/ECS/Core/DetailedPerformanceAnalysis.performance.test.ts create mode 100644 tests/ECS/Core/InitializationComparison.performance.test.ts create mode 100644 tests/ECS/Core/OptimizedPerformanceAnalysis.performance.test.ts diff --git a/src/ECS/Core/ComponentIndex.ts b/src/ECS/Core/ComponentIndex.ts index c40aa504..c95ebbe5 100644 --- a/src/ECS/Core/ComponentIndex.ts +++ b/src/ECS/Core/ComponentIndex.ts @@ -1,5 +1,4 @@ import { Entity } from '../Entity'; -import { Component } from '../Component'; import { ComponentType } from './ComponentStorage'; /** @@ -67,17 +66,29 @@ export class HashComponentIndex implements IComponentIndex { private _totalQueryTime = 0; private _lastUpdated = Date.now(); + private _setPool: Set[] = []; + private _componentTypeSetPool: Set[] = []; + public addEntity(entity: Entity): void { - const components = entity.components; - const componentTypes = new Set(); + if (entity.components.length === 0) { + const componentTypes = this._componentTypeSetPool.pop() || new Set(); + componentTypes.clear(); + this._entityToComponents.set(entity, componentTypes); + this._lastUpdated = Date.now(); + return; + } - for (const component of components) { + const componentTypes = this._componentTypeSetPool.pop() || new Set(); + componentTypes.clear(); + + for (const component of entity.components) { const componentType = component.constructor as ComponentType; componentTypes.add(componentType); let entities = this._componentToEntities.get(componentType); if (!entities) { - entities = new Set(); + entities = this._setPool.pop() || new Set(); + entities.clear(); this._componentToEntities.set(componentType, entities); } entities.add(entity); @@ -97,17 +108,28 @@ export class HashComponentIndex implements IComponentIndex { entities.delete(entity); if (entities.size === 0) { this._componentToEntities.delete(componentType); + if (this._setPool.length < 50) { + entities.clear(); + this._setPool.push(entities); + } } } } this._entityToComponents.delete(entity); + + if (this._componentTypeSetPool.length < 50) { + componentTypes.clear(); + this._componentTypeSetPool.push(componentTypes); + } + this._lastUpdated = Date.now(); } public query(componentType: ComponentType): Set { const startTime = performance.now(); - const result = new Set(this._componentToEntities.get(componentType) || []); + const entities = this._componentToEntities.get(componentType); + const result = entities ? new Set(entities) : new Set(); this._queryCount++; this._totalQueryTime += performance.now() - startTime; @@ -383,7 +405,10 @@ export class ComponentIndexManager { */ public addEntity(entity: Entity): void { this._activeIndex.addEntity(entity); - this.checkOptimization(); + + if (this._autoOptimize && this._activeIndex.getStats().queryCount % 100 === 0) { + this.checkOptimization(); + } } /** diff --git a/src/ECS/Core/EntityManager.ts b/src/ECS/Core/EntityManager.ts index a047468b..522c42be 100644 --- a/src/ECS/Core/EntityManager.ts +++ b/src/ECS/Core/EntityManager.ts @@ -416,9 +416,11 @@ export class EntityManager { this.updateNameIndex(entity, true); this.updateTagIndex(entity, true); - this._componentIndexManager.addEntity(entity); - this._archetypeSystem.addEntity(entity); - this._dirtyTrackingSystem.markDirty(entity, DirtyFlag.COMPONENT_ADDED); + if (entity.components.length > 0) { + this._componentIndexManager.addEntity(entity); + this._archetypeSystem.addEntity(entity); + this._dirtyTrackingSystem.markDirty(entity, DirtyFlag.COMPONENT_ADDED); + } // 发射实体创建事件 this._eventBus.emitEntityCreated({ @@ -470,9 +472,12 @@ export class EntityManager { for (const entity of entities) { this.updateNameIndex(entity, true); this.updateTagIndex(entity, true); - this._componentIndexManager.addEntity(entity); - this._archetypeSystem.addEntity(entity); - this._dirtyTrackingSystem.markDirty(entity, DirtyFlag.COMPONENT_ADDED); + + if (entity.components.length > 0) { + this._componentIndexManager.addEntity(entity); + this._archetypeSystem.addEntity(entity); + this._dirtyTrackingSystem.markDirty(entity, DirtyFlag.COMPONENT_ADDED); + } } // 批量发射事件 diff --git a/src/ECS/Utils/BigIntCompatibility.ts b/src/ECS/Utils/BigIntCompatibility.ts index 1a926fd6..4a6ffd54 100644 --- a/src/ECS/Utils/BigIntCompatibility.ts +++ b/src/ECS/Utils/BigIntCompatibility.ts @@ -507,6 +507,8 @@ class ArrayBigInt implements IBigIntLike { */ export class BigIntFactory { private static _supportsBigInt: boolean | null = null; + private static _cachedZero: IBigIntLike | null = null; + private static _cachedOne: IBigIntLike | null = null; // 缓存检测结果以避免重复检测 /** @@ -592,7 +594,10 @@ export class BigIntFactory { * @returns 零值的IBigIntLike实例 */ public static zero(): IBigIntLike { - return this.create(0); + if (!this._cachedZero) { + this._cachedZero = this.create(0); + } + return this._cachedZero; } /** @@ -600,7 +605,10 @@ export class BigIntFactory { * @returns 1值的IBigIntLike实例 */ public static one(): IBigIntLike { - return this.create(1); + if (!this._cachedOne) { + this._cachedOne = this.create(1); + } + return this._cachedOne; } /** diff --git a/tests/ECS/Core/ComponentIndexManager.test.ts b/tests/ECS/Core/ComponentIndexManager.test.ts new file mode 100644 index 00000000..b7254d22 --- /dev/null +++ b/tests/ECS/Core/ComponentIndexManager.test.ts @@ -0,0 +1,302 @@ +import { EntityManager } from '../../../src/ECS/Core/EntityManager'; +import { ComponentTypeManager } from '../../../src/ECS/Utils/ComponentTypeManager'; +import { Entity } from '../../../src/ECS/Entity'; +import { Component } from '../../../src/ECS/Component'; + +// 测试用组件 +class TestComponent extends Component { + constructor(public value: number = 0) { + super(); + } +} + +class AnotherTestComponent extends Component { + constructor(public name: string = 'test') { + super(); + } +} + +describe('ComponentIndexManager功能测试', () => { + let entityManager: EntityManager; + + beforeEach(() => { + ComponentTypeManager.instance.reset(); + entityManager = new EntityManager(); + }); + + describe('基本功能测试', () => { + test('应该能够正确创建空实体', () => { + const entity = entityManager.createEntity('TestEntity'); + + expect(entity).toBeDefined(); + expect(entity.name).toBe('TestEntity'); + expect(entity.components.length).toBe(0); + expect(entity.id).toBeGreaterThanOrEqual(0); + }); + + test('应该能够正确管理实体的组件索引', () => { + const entity = entityManager.createEntity('TestEntity'); + const component = new TestComponent(42); + + // 添加组件前 + expect(entity.hasComponent(TestComponent)).toBe(false); + + // 添加组件 + entity.addComponent(component); + + // 添加组件后 + expect(entity.hasComponent(TestComponent)).toBe(true); + expect(entity.getComponent(TestComponent)).toBe(component); + expect(entity.components.length).toBe(1); + }); + + test('应该能够正确处理组件查询', () => { + const entity1 = entityManager.createEntity('Entity1'); + const entity2 = entityManager.createEntity('Entity2'); + + entity1.addComponent(new TestComponent(1)); + entity2.addComponent(new TestComponent(2)); + entity2.addComponent(new AnotherTestComponent('test2')); + + // 查询包含TestComponent的实体 + const entitiesWithTest = entityManager.getEntitiesWithComponent(TestComponent); + expect(entitiesWithTest).toHaveLength(2); + expect(entitiesWithTest).toContain(entity1); + expect(entitiesWithTest).toContain(entity2); + + // 查询包含AnotherTestComponent的实体 + const entitiesWithAnother = entityManager.getEntitiesWithComponent(AnotherTestComponent); + expect(entitiesWithAnother).toHaveLength(1); + expect(entitiesWithAnother).toContain(entity2); + }); + + test('应该能够正确处理复杂查询', () => { + const entity1 = entityManager.createEntity('Entity1'); + const entity2 = entityManager.createEntity('Entity2'); + const entity3 = entityManager.createEntity('Entity3'); + + // entity1: TestComponent + entity1.addComponent(new TestComponent(1)); + + // entity2: TestComponent + AnotherTestComponent + entity2.addComponent(new TestComponent(2)); + entity2.addComponent(new AnotherTestComponent('test2')); + + // entity3: AnotherTestComponent + entity3.addComponent(new AnotherTestComponent('test3')); + + // AND查询:同时包含两个组件的实体 + const bothComponents = entityManager.queryWithComponentIndex( + [TestComponent, AnotherTestComponent], + 'AND' + ); + expect(bothComponents.size).toBe(1); + expect(bothComponents.has(entity2)).toBe(true); + + // OR查询:包含任一组件的实体 + const eitherComponent = entityManager.queryWithComponentIndex( + [TestComponent, AnotherTestComponent], + 'OR' + ); + expect(eitherComponent.size).toBe(3); + expect(eitherComponent.has(entity1)).toBe(true); + expect(eitherComponent.has(entity2)).toBe(true); + expect(eitherComponent.has(entity3)).toBe(true); + }); + }); + + describe('组件移除功能测试', () => { + test('应该能够正确移除组件并更新索引', () => { + const entity = entityManager.createEntity('TestEntity'); + const component = new TestComponent(42); + + // 添加组件 + entity.addComponent(component); + expect(entity.hasComponent(TestComponent)).toBe(true); + + // 移除组件 + entity.removeComponent(component); + expect(entity.hasComponent(TestComponent)).toBe(false); + expect(entity.getComponent(TestComponent)).toBeNull(); + expect(entity.components.length).toBe(0); + + // 索引应该被正确更新 + const entitiesWithTest = entityManager.getEntitiesWithComponent(TestComponent); + expect(entitiesWithTest).toHaveLength(0); + }); + + test('应该能够正确处理实体销毁', () => { + const entity = entityManager.createEntity('TestEntity'); + entity.addComponent(new TestComponent(42)); + + // 确认实体存在且有组件 + expect(entityManager.getEntity(entity.id)).toBe(entity); + expect(entityManager.getEntitiesWithComponent(TestComponent)).toHaveLength(1); + + // 销毁实体 + const destroyed = entityManager.destroyEntity(entity); + expect(destroyed).toBe(true); + + // 确认实体被正确销毁 + expect(entityManager.getEntity(entity.id)).toBeNull(); + expect(entityManager.getEntitiesWithComponent(TestComponent)).toHaveLength(0); + }); + }); + + describe('批量操作功能测试', () => { + test('应该能够正确处理批量创建实体', () => { + const entities = entityManager.createEntitiesBatch(5, 'BatchEntity'); + + expect(entities).toHaveLength(5); + entities.forEach((entity, index) => { + expect(entity.name).toMatch(/^BatchEntity_\d+$/); + expect(entity.components.length).toBe(0); + expect(entityManager.getEntity(entity.id)).toBe(entity); + }); + + expect(entityManager.entityCount).toBe(5); + }); + + test('批量创建的实体应该有正确的索引', () => { + const entities = entityManager.createEntitiesBatch(3, 'IndexTest'); + + // 给第一个和第三个实体添加组件 + entities[0].addComponent(new TestComponent(1)); + entities[2].addComponent(new TestComponent(3)); + + const entitiesWithTest = entityManager.getEntitiesWithComponent(TestComponent); + expect(entitiesWithTest).toHaveLength(2); + expect(entitiesWithTest).toContain(entities[0]); + expect(entitiesWithTest).toContain(entities[2]); + }); + }); + + describe('查询构建器功能测试', () => { + test('应该能够使用查询构建器进行复杂查询', () => { + const entity1 = entityManager.createEntity('Active1'); + const entity2 = entityManager.createEntity('Active2'); + const entity3 = entityManager.createEntity('Inactive'); + + entity1.addComponent(new TestComponent(1)); + entity1.active = true; + + entity2.addComponent(new TestComponent(2)); + entity2.addComponent(new AnotherTestComponent('test2')); + entity2.active = true; + + entity3.addComponent(new TestComponent(3)); + entity3.active = false; + + // 查询激活状态且包含TestComponent的实体 + const activeWithTest = entityManager.query() + .withAll(TestComponent) + .active() + .execute(); + + expect(activeWithTest).toHaveLength(2); + expect(activeWithTest).toContain(entity1); + expect(activeWithTest).toContain(entity2); + expect(activeWithTest).not.toContain(entity3); + + // 查询同时包含两个组件的实体 + const withBothComponents = entityManager.query() + .withAll(TestComponent, AnotherTestComponent) + .execute(); + + expect(withBothComponents).toHaveLength(1); + expect(withBothComponents).toContain(entity2); + }); + + test('查询构建器应该支持自定义过滤条件', () => { + const entity1 = entityManager.createEntity('Player1'); + const entity2 = entityManager.createEntity('Enemy1'); + const entity3 = entityManager.createEntity('Player2'); + + entity1.addComponent(new TestComponent(100)); + entity2.addComponent(new TestComponent(50)); + entity3.addComponent(new TestComponent(200)); + + // 查询名称以"Player"开头且TestComponent值大于150的实体 + const strongPlayers = entityManager.query() + .withAll(TestComponent) + .where(entity => entity.name.startsWith('Player')) + .where(entity => { + const testComp = entity.getComponent(TestComponent); + return testComp !== null && testComp.value > 150; + }) + .execute(); + + expect(strongPlayers).toHaveLength(1); + expect(strongPlayers).toContain(entity3); + }); + }); + + describe('统计信息功能测试', () => { + test('应该能够获取正确的统计信息', () => { + // 创建一些实体和组件 + const entity1 = entityManager.createEntity('StatTest1'); + const entity2 = entityManager.createEntity('StatTest2'); + + entity1.addComponent(new TestComponent(1)); + entity2.addComponent(new TestComponent(2)); + entity2.addComponent(new AnotherTestComponent('test')); + + const stats = entityManager.getOptimizationStats(); + + expect(stats).toBeDefined(); + expect(stats.componentIndex).toBeDefined(); + expect(stats.componentIndex.type).toBe('hash'); + expect(stats.archetypeSystem).toBeDefined(); + expect(stats.dirtyTracking).toBeDefined(); + }); + + test('应该能够正确统计实体数量', () => { + expect(entityManager.entityCount).toBe(0); + expect(entityManager.activeEntityCount).toBe(0); + + const entity1 = entityManager.createEntity('Count1'); + const entity2 = entityManager.createEntity('Count2'); + + expect(entityManager.entityCount).toBe(2); + expect(entityManager.activeEntityCount).toBe(2); + + entity1.active = false; + expect(entityManager.activeEntityCount).toBe(1); + + entityManager.destroyEntity(entity2); + expect(entityManager.entityCount).toBe(1); + expect(entityManager.activeEntityCount).toBe(0); + }); + }); + + describe('边界情况测试', () => { + test('应该能够处理空组件列表的查询', () => { + const entity = entityManager.createEntity('EmptyTest'); + + const emptyQuery = entityManager.queryWithComponentIndex([], 'AND'); + expect(emptyQuery.size).toBe(0); + + const emptyOrQuery = entityManager.queryWithComponentIndex([], 'OR'); + expect(emptyOrQuery.size).toBe(0); + }); + + test('应该能够处理不存在的组件类型查询', () => { + class NonExistentComponent extends Component {} + + const entities = entityManager.getEntitiesWithComponent(NonExistentComponent); + expect(entities).toHaveLength(0); + }); + + test('应该能够处理重复添加相同组件类型', () => { + const entity = entityManager.createEntity('DuplicateTest'); + const component1 = new TestComponent(1); + const component2 = new TestComponent(2); + + entity.addComponent(component1); + expect(() => entity.addComponent(component2)).toThrow(); + + // 第一个组件应该仍然存在 + expect(entity.getComponent(TestComponent)).toBe(component1); + }); + }); +}); \ No newline at end of file diff --git a/tests/ECS/Core/DetailedPerformanceAnalysis.performance.test.ts b/tests/ECS/Core/DetailedPerformanceAnalysis.performance.test.ts new file mode 100644 index 00000000..6347d2e0 --- /dev/null +++ b/tests/ECS/Core/DetailedPerformanceAnalysis.performance.test.ts @@ -0,0 +1,227 @@ +import { EntityManager } from '../../../src/ECS/Core/EntityManager'; +import { ComponentTypeManager } from '../../../src/ECS/Utils/ComponentTypeManager'; +import { Entity } from '../../../src/ECS/Entity'; + +describe('详细性能分析 - 逐步测量', () => { + let entityManager: EntityManager; + + beforeEach(() => { + ComponentTypeManager.instance.reset(); + entityManager = new EntityManager(); + }); + + test('精确测量createEntity中每个步骤的耗时', () => { + const testCount = 1000; + console.log(`\n=== 详细性能分析 (${testCount}个实体) ===`); + + const timings = { + idCheckOut: 0, + nameGeneration: 0, + entityConstruction: 0, + mapSet: 0, + nameIndexUpdate: 0, + tagIndexUpdate: 0, + componentIndexManager: 0, + archetypeSystem: 0, + dirtyTracking: 0, + eventEmission: 0, + total: 0 + }; + + const totalStart = performance.now(); + + for (let i = 0; i < testCount; i++) { + // 步骤1: ID分配 + let stepStart = performance.now(); + const id = entityManager['_identifierPool'].checkOut(); + timings.idCheckOut += performance.now() - stepStart; + + // 步骤2: 名称生成 + stepStart = performance.now(); + const name = `Entity_${id}`; + timings.nameGeneration += performance.now() - stepStart; + + // 步骤3: Entity构造 + stepStart = performance.now(); + const entity = new Entity(name, id); + timings.entityConstruction += performance.now() - stepStart; + + // 步骤4: Map存储 + stepStart = performance.now(); + entityManager['_entities'].set(id, entity); + timings.mapSet += performance.now() - stepStart; + + // 步骤5: 名称索引更新 + stepStart = performance.now(); + entityManager['updateNameIndex'](entity, true); + timings.nameIndexUpdate += performance.now() - stepStart; + + // 步骤6: 标签索引更新 + stepStart = performance.now(); + entityManager['updateTagIndex'](entity, true); + timings.tagIndexUpdate += performance.now() - stepStart; + + // 步骤7: 组件索引管理器 + stepStart = performance.now(); + entityManager['_componentIndexManager'].addEntity(entity); + timings.componentIndexManager += performance.now() - stepStart; + + // 步骤8: 原型系统 + stepStart = performance.now(); + entityManager['_archetypeSystem'].addEntity(entity); + timings.archetypeSystem += performance.now() - stepStart; + + // 步骤9: 脏标记系统 + stepStart = performance.now(); + entityManager['_dirtyTrackingSystem'].markDirty(entity, 1); // DirtyFlag.COMPONENT_ADDED + timings.dirtyTracking += performance.now() - stepStart; + + // 步骤10: 事件发射 + stepStart = performance.now(); + entityManager['_eventBus'].emitEntityCreated({ + timestamp: Date.now(), + source: 'EntityManager', + entityId: entity.id, + entityName: entity.name, + entityTag: entity.tag?.toString() + }); + timings.eventEmission += performance.now() - stepStart; + } + + timings.total = performance.now() - totalStart; + + console.log('\n各步骤耗时统计:'); + console.log(`总耗时: ${timings.total.toFixed(2)}ms`); + console.log(`平均每个实体: ${(timings.total / testCount).toFixed(3)}ms`); + console.log('\n详细分解:'); + + const sortedTimings = Object.entries(timings) + .filter(([key]) => key !== 'total') + .sort(([,a], [,b]) => b - a) + .map(([key, time]) => ({ + step: key, + timeMs: time, + percentage: (time / timings.total * 100), + avgPerEntity: (time / testCount * 1000) // 转换为微秒 + })); + + for (const timing of sortedTimings) { + console.log(` ${timing.step.padEnd(20)}: ${timing.timeMs.toFixed(2)}ms (${timing.percentage.toFixed(1)}%) - ${timing.avgPerEntity.toFixed(1)}μs/entity`); + } + + console.log('\n最耗时的前3个步骤:'); + for (let i = 0; i < Math.min(3, sortedTimings.length); i++) { + const timing = sortedTimings[i]; + console.log(` ${i + 1}. ${timing.step}: ${timing.percentage.toFixed(1)}% (${timing.timeMs.toFixed(2)}ms)`); + } + }); + + test('对比纯Entity创建和完整创建流程', () => { + const testCount = 1000; + console.log(`\n=== 创建方式对比 (${testCount}个实体) ===`); + + // 1. 纯Entity创建 + let startTime = performance.now(); + const pureEntities = []; + for (let i = 0; i < testCount; i++) { + pureEntities.push(new Entity(`Pure_${i}`, i)); + } + const pureTime = performance.now() - startTime; + + // 2. 完整EntityManager创建 + startTime = performance.now(); + const managedEntities = []; + for (let i = 0; i < testCount; i++) { + managedEntities.push(entityManager.createEntity(`Managed_${i}`)); + } + const managedTime = performance.now() - startTime; + + console.log(`纯Entity创建: ${pureTime.toFixed(2)}ms`); + console.log(`EntityManager创建: ${managedTime.toFixed(2)}ms`); + console.log(`性能差距: ${(managedTime / pureTime).toFixed(1)}倍`); + console.log(`管理开销: ${(managedTime - pureTime).toFixed(2)}ms (${((managedTime - pureTime) / managedTime * 100).toFixed(1)}%)`); + }); + + test('测量批量操作的效果', () => { + const testCount = 1000; + console.log(`\n=== 批量操作效果测试 (${testCount}个实体) ===`); + + // 1. 逐个处理 + let startTime = performance.now(); + for (let i = 0; i < testCount; i++) { + entityManager.createEntity(`Individual_${i}`); + } + const individualTime = performance.now() - startTime; + + entityManager = new EntityManager(); + + // 2. 批量处理 + startTime = performance.now(); + entityManager.createEntitiesBatch(testCount, "Batch", false); + const batchTime = performance.now() - startTime; + + entityManager = new EntityManager(); + + // 3. 批量处理(跳过事件) + startTime = performance.now(); + entityManager.createEntitiesBatch(testCount, "BatchNoEvents", true); + const batchNoEventsTime = performance.now() - startTime; + + console.log(`逐个创建: ${individualTime.toFixed(2)}ms`); + console.log(`批量创建: ${batchTime.toFixed(2)}ms`); + console.log(`批量创建(跳过事件): ${batchNoEventsTime.toFixed(2)}ms`); + console.log(`批量vs逐个: ${(individualTime / batchTime).toFixed(2)}x`); + console.log(`跳过事件优化: ${(batchTime / batchNoEventsTime).toFixed(2)}x`); + }); + + test('分析最耗时组件的内部实现', () => { + console.log(`\n=== 最耗时组件内部分析 ===`); + + const testCount = 500; // 较少数量以便详细分析 + + // 单独测试各个重要组件 + const entity = new Entity("TestEntity", 1); + + // 测试组件索引管理器 + let startTime = performance.now(); + for (let i = 0; i < testCount; i++) { + const testEntity = new Entity(`Test_${i}`, i); + entityManager['_componentIndexManager'].addEntity(testEntity); + } + const componentIndexTime = performance.now() - startTime; + + // 测试原型系统 + startTime = performance.now(); + for (let i = 0; i < testCount; i++) { + const testEntity = new Entity(`Test_${i}`, i + testCount); + entityManager['_archetypeSystem'].addEntity(testEntity); + } + const archetypeTime = performance.now() - startTime; + + // 测试脏标记系统 + startTime = performance.now(); + for (let i = 0; i < testCount; i++) { + const testEntity = new Entity(`Test_${i}`, i + testCount * 2); + entityManager['_dirtyTrackingSystem'].markDirty(testEntity, 1); + } + const dirtyTrackingTime = performance.now() - startTime; + + // 测试事件发射 + startTime = performance.now(); + for (let i = 0; i < testCount; i++) { + entityManager['_eventBus'].emitEntityCreated({ + timestamp: Date.now(), + source: 'EntityManager', + entityId: i, + entityName: `Event_${i}`, + entityTag: undefined + }); + } + const eventTime = performance.now() - startTime; + + console.log(`组件索引管理器: ${componentIndexTime.toFixed(2)}ms (${(componentIndexTime / testCount * 1000).toFixed(1)}μs/entity)`); + console.log(`原型系统: ${archetypeTime.toFixed(2)}ms (${(archetypeTime / testCount * 1000).toFixed(1)}μs/entity)`); + console.log(`脏标记系统: ${dirtyTrackingTime.toFixed(2)}ms (${(dirtyTrackingTime / testCount * 1000).toFixed(1)}μs/entity)`); + console.log(`事件发射: ${eventTime.toFixed(2)}ms (${(eventTime / testCount * 1000).toFixed(1)}μs/entity)`); + }); +}); \ No newline at end of file diff --git a/tests/ECS/Core/InitializationComparison.performance.test.ts b/tests/ECS/Core/InitializationComparison.performance.test.ts new file mode 100644 index 00000000..296a510f --- /dev/null +++ b/tests/ECS/Core/InitializationComparison.performance.test.ts @@ -0,0 +1,176 @@ +import { Entity } from '../../../src/ECS/Entity'; +import { BigIntFactory } from '../../../src/ECS/Utils/BigIntCompatibility'; +import { ComponentType } from '../../../src/ECS/Core/ComponentStorage'; + +describe('初始化方式性能对比', () => { + test('对比不同初始化方式的性能', () => { + const testCount = 10000; + console.log(`\n=== 初始化方式对比 (${testCount}个实体) ===`); + + // 方式1:字段直接初始化(原来的方式) + class EntityWithFieldInit { + public name: string; + public id: number; + private _componentMask = BigIntFactory.zero(); + private _componentTypeToIndex = new Map(); + + constructor(name: string, id: number) { + this.name = name; + this.id = id; + } + } + + // 方式2:构造函数初始化(新方式) + class EntityWithConstructorInit { + public name: string; + public id: number; + private _componentMask: any; + private _componentTypeToIndex: Map; + + constructor(name: string, id: number) { + this.name = name; + this.id = id; + this._componentMask = BigIntFactory.zero(); + this._componentTypeToIndex = new Map(); + } + } + + // 方式3:完全延迟初始化 + class EntityWithLazyInit { + public name: string; + public id: number; + private _componentMask: any; + private _componentTypeToIndex: Map | undefined; + + constructor(name: string, id: number) { + this.name = name; + this.id = id; + // 什么都不初始化 + } + + private ensureInit() { + if (!this._componentTypeToIndex) { + this._componentMask = BigIntFactory.zero(); + this._componentTypeToIndex = new Map(); + } + } + } + + // 测试方式1:字段直接初始化 + let startTime = performance.now(); + const entities1 = []; + for (let i = 0; i < testCount; i++) { + entities1.push(new EntityWithFieldInit(`Entity_${i}`, i)); + } + const fieldInitTime = performance.now() - startTime; + + // 测试方式2:构造函数初始化 + startTime = performance.now(); + const entities2 = []; + for (let i = 0; i < testCount; i++) { + entities2.push(new EntityWithConstructorInit(`Entity_${i}`, i)); + } + const constructorInitTime = performance.now() - startTime; + + // 测试方式3:延迟初始化 + startTime = performance.now(); + const entities3 = []; + for (let i = 0; i < testCount; i++) { + entities3.push(new EntityWithLazyInit(`Entity_${i}`, i)); + } + const lazyInitTime = performance.now() - startTime; + + // 测试方式4:只创建基本对象 + startTime = performance.now(); + const entities4 = []; + for (let i = 0; i < testCount; i++) { + entities4.push({ name: `Entity_${i}`, id: i }); + } + const basicObjectTime = performance.now() - startTime; + + console.log(`字段直接初始化: ${fieldInitTime.toFixed(2)}ms`); + console.log(`构造函数初始化: ${constructorInitTime.toFixed(2)}ms`); + console.log(`延迟初始化: ${lazyInitTime.toFixed(2)}ms`); + console.log(`基本对象创建: ${basicObjectTime.toFixed(2)}ms`); + + console.log(`\n性能对比:`); + console.log(`构造函数 vs 字段初始化: ${(fieldInitTime / constructorInitTime).toFixed(2)}x`); + console.log(`延迟 vs 构造函数: ${(constructorInitTime / lazyInitTime).toFixed(2)}x`); + console.log(`延迟 vs 基本对象: ${(lazyInitTime / basicObjectTime).toFixed(2)}x`); + }); + + test('测试BigIntFactory.zero()的性能', () => { + const testCount = 10000; + console.log(`\n=== BigIntFactory.zero()性能测试 ===`); + + // 测试1:每次调用BigIntFactory.zero() + let startTime = performance.now(); + const values1 = []; + for (let i = 0; i < testCount; i++) { + values1.push(BigIntFactory.zero()); + } + const directCallTime = performance.now() - startTime; + + // 测试2:重复使用同一个实例 + const sharedZero = BigIntFactory.zero(); + startTime = performance.now(); + const values2 = []; + for (let i = 0; i < testCount; i++) { + values2.push(sharedZero); + } + const sharedInstanceTime = performance.now() - startTime; + + // 测试3:使用数字0 + startTime = performance.now(); + const values3 = []; + for (let i = 0; i < testCount; i++) { + values3.push(0); + } + const numberZeroTime = performance.now() - startTime; + + console.log(`每次调用BigIntFactory.zero(): ${directCallTime.toFixed(2)}ms`); + console.log(`重复使用同一实例: ${sharedInstanceTime.toFixed(2)}ms`); + console.log(`使用数字0: ${numberZeroTime.toFixed(2)}ms`); + + console.log(`性能提升:`); + console.log(`共享实例 vs 每次调用: ${(directCallTime / sharedInstanceTime).toFixed(2)}x faster`); + console.log(`数字0 vs BigIntFactory: ${(directCallTime / numberZeroTime).toFixed(2)}x faster`); + }); + + test('测试Map创建的性能', () => { + const testCount = 10000; + console.log(`\n=== Map创建性能测试 ===`); + + // 测试1:每次new Map() + let startTime = performance.now(); + const maps1 = []; + for (let i = 0; i < testCount; i++) { + maps1.push(new Map()); + } + const newMapTime = performance.now() - startTime; + + // 测试2:使用对象字面量 + startTime = performance.now(); + const objects = []; + for (let i = 0; i < testCount; i++) { + objects.push({}); + } + const objectTime = performance.now() - startTime; + + // 测试3:延迟创建Map + startTime = performance.now(); + const lazyMaps = []; + for (let i = 0; i < testCount; i++) { + lazyMaps.push(null); // 先不创建 + } + const lazyTime = performance.now() - startTime; + + console.log(`每次new Map(): ${newMapTime.toFixed(2)}ms`); + console.log(`对象字面量: ${objectTime.toFixed(2)}ms`); + console.log(`延迟创建: ${lazyTime.toFixed(2)}ms`); + + console.log(`性能对比:`); + console.log(`对象 vs Map: ${(newMapTime / objectTime).toFixed(2)}x faster`); + console.log(`延迟 vs Map: ${(newMapTime / lazyTime).toFixed(2)}x faster`); + }); +}); \ No newline at end of file diff --git a/tests/ECS/Core/OptimizedPerformanceAnalysis.performance.test.ts b/tests/ECS/Core/OptimizedPerformanceAnalysis.performance.test.ts new file mode 100644 index 00000000..c6f65d4d --- /dev/null +++ b/tests/ECS/Core/OptimizedPerformanceAnalysis.performance.test.ts @@ -0,0 +1,171 @@ +import { EntityManager } from '../../../src/ECS/Core/EntityManager'; +import { ComponentTypeManager } from '../../../src/ECS/Utils/ComponentTypeManager'; +import { Entity } from '../../../src/ECS/Entity'; + +describe('优化后的性能分析 - ComponentIndexManager优化', () => { + let entityManager: EntityManager; + + beforeEach(() => { + ComponentTypeManager.instance.reset(); + entityManager = new EntityManager(); + }); + + test('测试优化后的实体创建性能', () => { + const testCount = 10000; + console.log(`\n=== 优化后的实体创建性能测试 (${testCount}个实体) ===`); + + const startTime = performance.now(); + const entities = []; + + for (let i = 0; i < testCount; i++) { + entities.push(entityManager.createEntity(`Entity_${i}`)); + } + + const totalTime = performance.now() - startTime; + const avgTime = totalTime / testCount; + + console.log(`总耗时: ${totalTime.toFixed(2)}ms`); + console.log(`平均每个实体: ${avgTime.toFixed(3)}ms`); + console.log(`每秒创建实体数: ${Math.round(1000 / avgTime)}`); + + if (totalTime < 140) { + console.log(`✅ 性能优化成功!实际耗时 ${totalTime.toFixed(2)}ms < 140ms 目标`); + } else { + console.log(`❌ 仍需进一步优化,实际耗时 ${totalTime.toFixed(2)}ms >= 140ms 目标`); + } + + // 性能基准:应该在140ms以下 + expect(totalTime).toBeLessThan(200); // 放宽一些给CI环境 + }); + + test('对比批量创建与逐个创建的性能', () => { + const testCount = 5000; + console.log(`\n=== 批量创建vs逐个创建对比 (${testCount}个实体) ===`); + + // 逐个创建 + let startTime = performance.now(); + for (let i = 0; i < testCount; i++) { + entityManager.createEntity(`Individual_${i}`); + } + const individualTime = performance.now() - startTime; + + // 重置管理器 + entityManager = new EntityManager(); + + // 批量创建 + startTime = performance.now(); + entityManager.createEntitiesBatch(testCount, "Batch", false); + const batchTime = performance.now() - startTime; + + // 重置管理器 + entityManager = new EntityManager(); + + // 批量创建(跳过事件) + startTime = performance.now(); + entityManager.createEntitiesBatch(testCount, "BatchNoEvents", true); + const batchNoEventsTime = performance.now() - startTime; + + console.log(`逐个创建: ${individualTime.toFixed(2)}ms`); + console.log(`批量创建: ${batchTime.toFixed(2)}ms`); + console.log(`批量创建(跳过事件): ${batchNoEventsTime.toFixed(2)}ms`); + console.log(`批量优化倍数: ${(individualTime / batchTime).toFixed(2)}x`); + console.log(`跳过事件优化倍数: ${(individualTime / batchNoEventsTime).toFixed(2)}x`); + }); + + test('测试组件索引管理器对空实体的优化效果', () => { + const testCount = 10000; + console.log(`\n=== 空实体优化效果测试 (${testCount}个空实体) ===`); + + const startTime = performance.now(); + const entities = []; + + for (let i = 0; i < testCount; i++) { + const entity = entityManager.createEntity(`EmptyEntity_${i}`); + entities.push(entity); + } + + const totalTime = performance.now() - startTime; + + // 验证前几个实体确实没有组件 + for (let i = 0; i < Math.min(5, entities.length); i++) { + expect(entities[i].components.length).toBe(0); + } + + console.log(`空实体创建总耗时: ${totalTime.toFixed(2)}ms`); + console.log(`平均每个空实体: ${(totalTime / testCount).toFixed(3)}ms`); + + // 获取优化统计信息 + const stats = entityManager.getOptimizationStats(); + console.log(`组件索引统计:`, stats.componentIndex); + + // 空实体创建应该非常快,放宽限制以适应CI环境 + expect(totalTime).toBeLessThan(150); + }); + + test('测试Set对象池的效果', () => { + const testCount = 1000; + console.log(`\n=== Set对象池效果测试 (${testCount}次添加/删除) ===`); + + // 创建实体 + const entities = []; + for (let i = 0; i < testCount; i++) { + entities.push(entityManager.createEntity(`PoolTest_${i}`)); + } + + // 测试删除和重新创建的性能 + const startTime = performance.now(); + + // 删除一半实体 + for (let i = 0; i < testCount / 2; i++) { + entityManager.destroyEntity(entities[i]); + } + + // 重新创建实体 + for (let i = 0; i < testCount / 2; i++) { + entityManager.createEntity(`RecycledEntity_${i}`); + } + + const totalTime = performance.now() - startTime; + + console.log(`删除+重新创建耗时: ${totalTime.toFixed(2)}ms`); + console.log(`平均每次操作: ${(totalTime / testCount).toFixed(3)}ms`); + + // 对象池优化应该让重复操作更快,放宽限制适应不同环境 + expect(totalTime).toBeLessThan(100); + }); + + test('内存使用量分析', () => { + const testCount = 5000; + console.log(`\n=== 内存使用量分析 (${testCount}个实体) ===`); + + // 获取初始内存使用情况 + const initialStats = entityManager.getOptimizationStats(); + const initialMemory = initialStats.componentIndex.memoryUsage; + + // 创建实体 + const entities = []; + for (let i = 0; i < testCount; i++) { + entities.push(entityManager.createEntity(`MemoryTest_${i}`)); + } + + // 获取创建后的内存使用情况 + const afterStats = entityManager.getOptimizationStats(); + const afterMemory = afterStats.componentIndex.memoryUsage; + + console.log(`初始内存使用: ${initialMemory} 字节`); + console.log(`创建后内存使用: ${afterMemory} 字节`); + console.log(`增加的内存: ${afterMemory - initialMemory} 字节`); + console.log(`平均每个实体内存: ${((afterMemory - initialMemory) / testCount).toFixed(2)} 字节`); + + // 清理并观察内存回收 + for (const entity of entities) { + entityManager.destroyEntity(entity); + } + + const cleanupStats = entityManager.getOptimizationStats(); + const cleanupMemory = cleanupStats.componentIndex.memoryUsage; + + console.log(`清理后内存使用: ${cleanupMemory} 字节`); + console.log(`内存回收率: ${(((afterMemory - cleanupMemory) / (afterMemory - initialMemory)) * 100).toFixed(1)}%`); + }); +}); \ No newline at end of file