优化createEntity的性能/新增批量创建实体api
This commit is contained in:
60
tests/ECS/Core/BatchCreateTest.test.ts
Normal file
60
tests/ECS/Core/BatchCreateTest.test.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import { EntityManager } from '../../../src/ECS/Core/EntityManager';
|
||||
import { ComponentTypeManager } from '../../../src/ECS/Utils/ComponentTypeManager';
|
||||
|
||||
describe('批量创建功能测试', () => {
|
||||
let entityManager: EntityManager;
|
||||
|
||||
beforeEach(() => {
|
||||
ComponentTypeManager.instance.reset();
|
||||
entityManager = new EntityManager();
|
||||
});
|
||||
|
||||
test('批量创建实体应该使用ID作为名称', () => {
|
||||
const entities = entityManager.createEntitiesBatch(5, "Test");
|
||||
|
||||
expect(entities).toHaveLength(5);
|
||||
|
||||
// 验证实体名称使用ID而不是索引
|
||||
for (const entity of entities) {
|
||||
expect(entity.name).toBe(`Test_${entity.id}`);
|
||||
}
|
||||
|
||||
// 验证ID是唯一的
|
||||
const ids = entities.map(e => e.id);
|
||||
const uniqueIds = new Set(ids);
|
||||
expect(uniqueIds.size).toBe(5);
|
||||
});
|
||||
|
||||
test('单个创建实体应该使用ID作为默认名称', () => {
|
||||
const entity1 = entityManager.createEntity();
|
||||
const entity2 = entityManager.createEntity();
|
||||
const entity3 = entityManager.createEntity("CustomName");
|
||||
|
||||
expect(entity1.name).toBe(`Entity_${entity1.id}`);
|
||||
expect(entity2.name).toBe(`Entity_${entity2.id}`);
|
||||
expect(entity3.name).toBe("CustomName");
|
||||
|
||||
// 确保ID是连续的或至少是唯一的
|
||||
expect(entity1.id).not.toBe(entity2.id);
|
||||
expect(entity2.id).not.toBe(entity3.id);
|
||||
});
|
||||
|
||||
test('混合创建方式的名称应该一致', () => {
|
||||
// 先单个创建
|
||||
const single = entityManager.createEntity();
|
||||
|
||||
// 再批量创建
|
||||
const batch = entityManager.createEntitiesBatch(3, "Batch");
|
||||
|
||||
// 再单个创建
|
||||
const single2 = entityManager.createEntity();
|
||||
|
||||
// 验证名称格式一致
|
||||
expect(single.name).toBe(`Entity_${single.id}`);
|
||||
expect(single2.name).toBe(`Entity_${single2.id}`);
|
||||
|
||||
for (const entity of batch) {
|
||||
expect(entity.name).toBe(`Batch_${entity.id}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
226
tests/ECS/Core/EntityCreationPerformance.test.ts
Normal file
226
tests/ECS/Core/EntityCreationPerformance.test.ts
Normal file
@@ -0,0 +1,226 @@
|
||||
import { EntityManager } from '../../../src/ECS/Core/EntityManager';
|
||||
import { ComponentTypeManager } from '../../../src/ECS/Utils/ComponentTypeManager';
|
||||
|
||||
describe('实体创建性能分析', () => {
|
||||
let entityManager: EntityManager;
|
||||
|
||||
beforeEach(() => {
|
||||
ComponentTypeManager.instance.reset();
|
||||
entityManager = new EntityManager();
|
||||
});
|
||||
|
||||
test('性能分析:创建10000个实体', () => {
|
||||
const entityCount = 10000;
|
||||
console.log(`开始创建 ${entityCount} 个实体...`);
|
||||
|
||||
// 预热
|
||||
for (let i = 0; i < 100; i++) {
|
||||
entityManager.createEntity(`Warmup_${i}`);
|
||||
}
|
||||
// 重新创建EntityManager来清理
|
||||
entityManager = new EntityManager();
|
||||
|
||||
// 测试不同的创建方式
|
||||
console.log('\n=== 性能对比测试 ===');
|
||||
|
||||
// 1. 使用默认名称(包含Date.now())
|
||||
let startTime = performance.now();
|
||||
const entitiesWithDefaultName: any[] = [];
|
||||
for (let i = 0; i < entityCount; i++) {
|
||||
entitiesWithDefaultName.push(entityManager.createEntity());
|
||||
}
|
||||
let endTime = performance.now();
|
||||
console.log(`1. 默认名称创建: ${(endTime - startTime).toFixed(2)}ms`);
|
||||
entityManager = new EntityManager();
|
||||
|
||||
// 2. 使用预设名称(避免Date.now())
|
||||
startTime = performance.now();
|
||||
const entitiesWithPresetName: any[] = [];
|
||||
for (let i = 0; i < entityCount; i++) {
|
||||
entitiesWithPresetName.push(entityManager.createEntity(`Entity_${i}`));
|
||||
}
|
||||
endTime = performance.now();
|
||||
console.log(`2. 预设名称创建: ${(endTime - startTime).toFixed(2)}ms`);
|
||||
entityManager = new EntityManager();
|
||||
|
||||
// 3. 使用相同名称(减少字符串创建)
|
||||
startTime = performance.now();
|
||||
const entitiesWithSameName: any[] = [];
|
||||
const sameName = 'SameName';
|
||||
for (let i = 0; i < entityCount; i++) {
|
||||
entitiesWithSameName.push(entityManager.createEntity(sameName));
|
||||
}
|
||||
endTime = performance.now();
|
||||
console.log(`3. 相同名称创建: ${(endTime - startTime).toFixed(2)}ms`);
|
||||
entityManager = new EntityManager();
|
||||
|
||||
// 4. 直接创建Entity对象(绕过EntityManager)
|
||||
startTime = performance.now();
|
||||
const directEntities: any[] = [];
|
||||
for (let i = 0; i < entityCount; i++) {
|
||||
// 直接创建Entity,不通过EntityManager的复杂逻辑
|
||||
directEntities.push(new (require('../../../src/ECS/Entity').Entity)(`Direct_${i}`, i));
|
||||
}
|
||||
endTime = performance.now();
|
||||
console.log(`4. 直接创建Entity: ${(endTime - startTime).toFixed(2)}ms`);
|
||||
|
||||
console.log('\n=== 性能分析结论 ===');
|
||||
console.log('如果相同名称创建明显更快,说明字符串操作是瓶颈');
|
||||
console.log('如果直接创建Entity更快,说明EntityManager的逻辑太重');
|
||||
});
|
||||
|
||||
test('详细分析EntityManager中的性能瓶颈', () => {
|
||||
const entityCount = 1000; // 较小数量便于分析
|
||||
|
||||
console.log('\n=== 详细性能分析 ===');
|
||||
|
||||
// 分析各个步骤的耗时
|
||||
let totalTime = 0;
|
||||
const stepTimes: Record<string, number> = {};
|
||||
|
||||
for (let i = 0; i < entityCount; i++) {
|
||||
const stepStart = performance.now();
|
||||
|
||||
// 模拟EntityManager.createEntity的各个步骤
|
||||
const name = `PerfTest_${i}`;
|
||||
|
||||
// 步骤1: ID分配
|
||||
let stepTime = performance.now();
|
||||
const id = entityManager['_identifierPool'].checkOut();
|
||||
stepTimes['ID分配'] = (stepTimes['ID分配'] || 0) + (performance.now() - stepTime);
|
||||
|
||||
// 步骤2: Entity创建
|
||||
stepTime = performance.now();
|
||||
const entity = new (require('../../../src/ECS/Entity').Entity)(name, id);
|
||||
stepTimes['Entity创建'] = (stepTimes['Entity创建'] || 0) + (performance.now() - stepTime);
|
||||
|
||||
// 步骤3: 各种索引更新
|
||||
stepTime = performance.now();
|
||||
entityManager['_entities'].set(id, entity);
|
||||
stepTimes['Map存储'] = (stepTimes['Map存储'] || 0) + (performance.now() - stepTime);
|
||||
|
||||
stepTime = performance.now();
|
||||
entityManager['updateNameIndex'](entity, true);
|
||||
stepTimes['名称索引'] = (stepTimes['名称索引'] || 0) + (performance.now() - stepTime);
|
||||
|
||||
stepTime = performance.now();
|
||||
entityManager['updateTagIndex'](entity, true);
|
||||
stepTimes['标签索引'] = (stepTimes['标签索引'] || 0) + (performance.now() - stepTime);
|
||||
|
||||
stepTime = performance.now();
|
||||
entityManager['_componentIndexManager'].addEntity(entity);
|
||||
stepTimes['组件索引'] = (stepTimes['组件索引'] || 0) + (performance.now() - stepTime);
|
||||
|
||||
stepTime = performance.now();
|
||||
entityManager['_archetypeSystem'].addEntity(entity);
|
||||
stepTimes['原型系统'] = (stepTimes['原型系统'] || 0) + (performance.now() - stepTime);
|
||||
|
||||
stepTime = performance.now();
|
||||
entityManager['_dirtyTrackingSystem'].markDirty(entity, 1); // DirtyFlag.COMPONENT_ADDED
|
||||
stepTimes['脏标记'] = (stepTimes['脏标记'] || 0) + (performance.now() - stepTime);
|
||||
|
||||
stepTime = performance.now();
|
||||
// 跳过事件发射,因为它涉及复杂的对象创建
|
||||
stepTimes['其他'] = (stepTimes['其他'] || 0) + (performance.now() - stepTime);
|
||||
|
||||
totalTime += (performance.now() - stepStart);
|
||||
}
|
||||
|
||||
console.log(`总耗时: ${totalTime.toFixed(2)}ms`);
|
||||
console.log('各步骤平均耗时:');
|
||||
for (const [step, time] of Object.entries(stepTimes)) {
|
||||
console.log(` ${step}: ${(time / entityCount * 1000).toFixed(3)}μs/entity`);
|
||||
}
|
||||
|
||||
// 找出最耗时的步骤
|
||||
const maxTime = Math.max(...Object.values(stepTimes));
|
||||
const slowestStep = Object.entries(stepTimes).find(([_, time]) => time === maxTime)?.[0];
|
||||
console.log(`最耗时的步骤: ${slowestStep} (${(maxTime / entityCount * 1000).toFixed(3)}μs/entity)`);
|
||||
});
|
||||
|
||||
test('测试批量创建优化方案', () => {
|
||||
const entityCount = 10000;
|
||||
console.log(`\n=== 批量创建优化测试 ===`);
|
||||
|
||||
// 当前方式:逐个创建
|
||||
let startTime = performance.now();
|
||||
for (let i = 0; i < entityCount; i++) {
|
||||
entityManager.createEntity(`Current_${i}`);
|
||||
}
|
||||
let endTime = performance.now();
|
||||
const currentTime = endTime - startTime;
|
||||
console.log(`当前方式: ${currentTime.toFixed(2)}ms`);
|
||||
|
||||
entityManager = new EntityManager();
|
||||
|
||||
// 如果有批量创建方法的话...
|
||||
// (这里只是演示概念,实际的批量创建需要在EntityManager中实现)
|
||||
console.log('建议:实现批量创建方法,减少重复的索引更新和事件发射');
|
||||
});
|
||||
|
||||
test('验证批量创建优化效果', () => {
|
||||
const entityCount = 10000;
|
||||
console.log(`\n=== 批量创建优化效果验证 ===`);
|
||||
|
||||
// 测试新的批量创建方法
|
||||
let startTime = performance.now();
|
||||
const batchEntities = entityManager.createEntitiesBatch(entityCount, "Batch", false);
|
||||
let endTime = performance.now();
|
||||
const batchTime = endTime - startTime;
|
||||
console.log(`批量创建(含事件): ${batchTime.toFixed(2)}ms`);
|
||||
|
||||
entityManager = new EntityManager();
|
||||
|
||||
// 测试跳过事件的批量创建
|
||||
startTime = performance.now();
|
||||
const batchEntitiesNoEvents = entityManager.createEntitiesBatch(entityCount, "BatchNoEvents", true);
|
||||
endTime = performance.now();
|
||||
const batchTimeNoEvents = endTime - startTime;
|
||||
console.log(`批量创建(跳过事件): ${batchTimeNoEvents.toFixed(2)}ms`);
|
||||
|
||||
entityManager = new EntityManager();
|
||||
|
||||
// 对比单个创建(使用优化后的createEntity)
|
||||
startTime = performance.now();
|
||||
const singleEntities: any[] = [];
|
||||
for (let i = 0; i < entityCount; i++) {
|
||||
singleEntities.push(entityManager.createEntity(`Single_${i}`));
|
||||
}
|
||||
endTime = performance.now();
|
||||
const singleTime = endTime - startTime;
|
||||
console.log(`优化后单个创建: ${singleTime.toFixed(2)}ms`);
|
||||
|
||||
console.log(`\n性能提升:`);
|
||||
console.log(`批量创建 vs 单个创建: ${(singleTime / batchTime).toFixed(1)}x faster`);
|
||||
console.log(`批量创建(跳过事件) vs 单个创建: ${(singleTime / batchTimeNoEvents).toFixed(1)}x faster`);
|
||||
|
||||
// 验证功能正确性
|
||||
expect(batchEntities.length).toBe(entityCount);
|
||||
expect(batchEntitiesNoEvents.length).toBe(entityCount);
|
||||
expect(singleEntities.length).toBe(entityCount);
|
||||
});
|
||||
|
||||
test('验证createEntity的Date.now()优化', () => {
|
||||
console.log(`\n=== createEntity优化验证 ===`);
|
||||
|
||||
const testCount = 1000;
|
||||
|
||||
// 测试优化后的默认名称生成
|
||||
let startTime = performance.now();
|
||||
for (let i = 0; i < testCount; i++) {
|
||||
entityManager.createEntity(); // 使用优化后的计数器命名
|
||||
}
|
||||
let endTime = performance.now();
|
||||
console.log(`计数器命名: ${(endTime - startTime).toFixed(2)}ms`);
|
||||
|
||||
entityManager = new EntityManager();
|
||||
|
||||
// 对比:模拟使用Date.now()的方式
|
||||
startTime = performance.now();
|
||||
for (let i = 0; i < testCount; i++) {
|
||||
entityManager.createEntity(`Entity_${Date.now()}_${i}`); // 模拟原来的方式
|
||||
}
|
||||
endTime = performance.now();
|
||||
console.log(`Date.now()命名: ${(endTime - startTime).toFixed(2)}ms`);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user