Files
esengine/tests/Utils/Serialization/Performance.test.ts

441 lines
17 KiB
TypeScript
Raw Normal View History

/**
* Protobuf序列化性能测试
*/
import { Component } from '../../../src/ECS/Component';
import { Entity } from '../../../src/ECS/Entity';
import { Scene } from '../../../src/ECS/Scene';
import { SnapshotManager } from '../../../src/Utils/Snapshot/SnapshotManager';
import { ProtobufSerializer } from '../../../src/Utils/Serialization/ProtobufSerializer';
import {
ProtoSerializable,
ProtoFloat,
ProtoInt32,
ProtoString,
ProtoBool
} from '../../../src/Utils/Serialization/ProtobufDecorators';
// 性能测试组件
@ProtoSerializable('PerfPosition')
class PerfPositionComponent extends Component {
@ProtoFloat(1) public x: number = 0;
@ProtoFloat(2) public y: number = 0;
@ProtoFloat(3) public z: number = 0;
constructor(x: number = 0, y: number = 0, z: number = 0) {
super();
this.x = x;
this.y = y;
this.z = z;
}
}
@ProtoSerializable('PerfVelocity')
class PerfVelocityComponent extends Component {
@ProtoFloat(1) public vx: number = 0;
@ProtoFloat(2) public vy: number = 0;
@ProtoFloat(3) public vz: number = 0;
constructor(vx: number = 0, vy: number = 0, vz: number = 0) {
super();
this.vx = vx;
this.vy = vy;
this.vz = vz;
}
}
@ProtoSerializable('PerfHealth')
class PerfHealthComponent extends Component {
@ProtoInt32(1) public maxHealth: number = 100;
@ProtoInt32(2) public currentHealth: number = 100;
@ProtoBool(3) public isDead: boolean = false;
@ProtoFloat(4) public regenerationRate: number = 0.5;
constructor(maxHealth: number = 100) {
super();
this.maxHealth = maxHealth;
this.currentHealth = maxHealth;
}
}
@ProtoSerializable('PerfPlayer')
class PerfPlayerComponent extends Component {
@ProtoString(1) public name: string = '';
@ProtoInt32(2) public level: number = 1;
@ProtoInt32(3) public experience: number = 0;
@ProtoInt32(4) public score: number = 0;
@ProtoBool(5) public isOnline: boolean = true;
constructor(name: string = 'Player', level: number = 1) {
super();
this.name = name;
this.level = level;
}
}
// 传统JSON序列化组件用于对比
class JsonPositionComponent extends Component {
public x: number = 0;
public y: number = 0;
public z: number = 0;
constructor(x: number = 0, y: number = 0, z: number = 0) {
super();
this.x = x;
this.y = y;
this.z = z;
}
}
class JsonPlayerComponent extends Component {
public name: string = '';
public level: number = 1;
public experience: number = 0;
public score: number = 0;
public isOnline: boolean = true;
constructor(name: string = 'Player', level: number = 1) {
super();
this.name = name;
this.level = level;
}
}
// Mock protobuf.js for performance testing
const createMockProtobuf = () => {
const mockEncodedData = new Uint8Array(32); // 模拟32字节的编码数据
mockEncodedData.fill(1);
return {
parse: jest.fn().mockReturnValue({
root: {
lookupType: jest.fn().mockImplementation((typeName: string) => ({
verify: jest.fn().mockReturnValue(null),
create: jest.fn().mockImplementation((data) => data),
encode: jest.fn().mockReturnValue({
finish: jest.fn().mockReturnValue(mockEncodedData)
}),
decode: jest.fn().mockReturnValue({
x: 10, y: 20, z: 30,
vx: 1, vy: 2, vz: 3,
maxHealth: 100, currentHealth: 80, isDead: false, regenerationRate: 0.5,
name: 'TestPlayer', level: 5, experience: 1000, score: 5000, isOnline: true
}),
toObject: jest.fn().mockImplementation((message) => message)
}))
}
})
};
};
describe('Protobuf序列化性能测试', () => {
let protobufSerializer: ProtobufSerializer;
let snapshotManager: SnapshotManager;
let scene: Scene;
beforeEach(() => {
protobufSerializer = ProtobufSerializer.getInstance();
protobufSerializer.initialize(createMockProtobuf());
snapshotManager = new SnapshotManager();
snapshotManager.initializeProtobuf(createMockProtobuf());
scene = new Scene();
jest.clearAllMocks();
});
describe('单组件序列化性能', () => {
const iterations = 1000;
it('应该比较protobuf和JSON序列化速度', () => {
const protobufComponents: PerfPositionComponent[] = [];
const jsonComponents: JsonPositionComponent[] = [];
// 准备测试数据
for (let i = 0; i < iterations; i++) {
protobufComponents.push(new PerfPositionComponent(
Math.random() * 1000,
Math.random() * 1000,
Math.random() * 100
));
jsonComponents.push(new JsonPositionComponent(
Math.random() * 1000,
Math.random() * 1000,
Math.random() * 100
));
}
// 测试Protobuf序列化
const protobufStartTime = performance.now();
let protobufTotalSize = 0;
for (const component of protobufComponents) {
const result = protobufSerializer.serialize(component);
protobufTotalSize += result.size;
}
const protobufEndTime = performance.now();
const protobufTime = protobufEndTime - protobufStartTime;
// 测试JSON序列化
const jsonStartTime = performance.now();
let jsonTotalSize = 0;
for (const component of jsonComponents) {
const jsonString = JSON.stringify({
x: component.x,
y: component.y,
z: component.z
});
jsonTotalSize += new Blob([jsonString]).size;
}
const jsonEndTime = performance.now();
const jsonTime = jsonEndTime - jsonStartTime;
// 性能断言
console.log(`\\n=== 单组件序列化性能对比 (${iterations} 次迭代) ===`);
console.log(`Protobuf时间: ${protobufTime.toFixed(2)}ms`);
console.log(`JSON时间: ${jsonTime.toFixed(2)}ms`);
console.log(`Protobuf总大小: ${protobufTotalSize} bytes`);
console.log(`JSON总大小: ${jsonTotalSize} bytes`);
if (jsonTime > 0) {
const speedImprovement = ((jsonTime - protobufTime) / jsonTime * 100);
console.log(`速度提升: ${speedImprovement.toFixed(1)}%`);
}
if (jsonTotalSize > 0) {
const sizeReduction = ((jsonTotalSize - protobufTotalSize) / jsonTotalSize * 100);
console.log(`大小减少: ${sizeReduction.toFixed(1)}%`);
}
// 基本性能验证
expect(protobufTime).toBeLessThan(1000); // 不应该超过1秒
expect(jsonTime).toBeLessThan(1000);
expect(protobufTotalSize).toBeGreaterThan(0);
expect(jsonTotalSize).toBeGreaterThan(0);
});
it('应该测试复杂组件的序列化性能', () => {
const protobufPlayers: PerfPlayerComponent[] = [];
const jsonPlayers: JsonPlayerComponent[] = [];
// 创建测试数据
for (let i = 0; i < iterations; i++) {
protobufPlayers.push(new PerfPlayerComponent(
`Player${i}`,
Math.floor(Math.random() * 100) + 1
));
jsonPlayers.push(new JsonPlayerComponent(
`Player${i}`,
Math.floor(Math.random() * 100) + 1
));
}
// Protobuf序列化测试
const protobufStart = performance.now();
for (const player of protobufPlayers) {
protobufSerializer.serialize(player);
}
const protobufTime = performance.now() - protobufStart;
// JSON序列化测试
const jsonStart = performance.now();
for (const player of jsonPlayers) {
JSON.stringify({
name: player.name,
level: player.level,
experience: player.experience,
score: player.score,
isOnline: player.isOnline
});
}
const jsonTime = performance.now() - jsonStart;
console.log(`\\n=== 复杂组件序列化性能 (${iterations} 次迭代) ===`);
console.log(`Protobuf时间: ${protobufTime.toFixed(2)}ms`);
console.log(`JSON时间: ${jsonTime.toFixed(2)}ms`);
expect(protobufTime).toBeLessThan(1000);
expect(jsonTime).toBeLessThan(1000);
});
});
describe('批量实体序列化性能', () => {
it('应该测试大量实体的快照创建性能', () => {
const entityCount = 100;
const entities: Entity[] = [];
// 创建测试实体
for (let i = 0; i < entityCount; i++) {
const entity = scene.createEntity(`Entity${i}`);
entity.addComponent(new PerfPositionComponent(
Math.random() * 1000,
Math.random() * 1000,
Math.random() * 100
));
entity.addComponent(new PerfVelocityComponent(
Math.random() * 10 - 5,
Math.random() * 10 - 5,
Math.random() * 2 - 1
));
entity.addComponent(new PerfHealthComponent(100 + Math.floor(Math.random() * 50)));
entity.addComponent(new PerfPlayerComponent(`Player${i}`, Math.floor(Math.random() * 50) + 1));
entities.push(entity);
}
// 测试快照创建性能
const snapshotStart = performance.now();
const snapshot = snapshotManager.createSceneSnapshot(entities);
const snapshotTime = performance.now() - snapshotStart;
console.log(`\\n=== 批量实体序列化性能 ===`);
console.log(`实体数量: ${entityCount}`);
console.log(`每个实体组件数: 4`);
console.log(`总组件数: ${entityCount * 4}`);
console.log(`快照创建时间: ${snapshotTime.toFixed(2)}ms`);
console.log(`平均每组件时间: ${(snapshotTime / (entityCount * 4)).toFixed(3)}ms`);
expect(snapshot.entities).toHaveLength(entityCount);
expect(snapshotTime).toBeLessThan(5000); // 不应该超过5秒
// 计算快照大小
let totalSnapshotSize = 0;
for (const entitySnapshot of snapshot.entities) {
for (const componentSnapshot of entitySnapshot.components) {
if (componentSnapshot.data && typeof componentSnapshot.data === 'object' && 'size' in componentSnapshot.data) {
totalSnapshotSize += (componentSnapshot.data as any).size;
}
}
}
console.log(`快照总大小: ${totalSnapshotSize} bytes`);
console.log(`平均每实体大小: ${(totalSnapshotSize / entityCount).toFixed(1)} bytes`);
expect(totalSnapshotSize).toBeGreaterThan(0);
});
});
describe('反序列化性能', () => {
it('应该测试快照恢复性能', () => {
const entityCount = 50;
const originalEntities: Entity[] = [];
// 创建原始实体
for (let i = 0; i < entityCount; i++) {
const entity = scene.createEntity(`Original${i}`);
entity.addComponent(new PerfPositionComponent(i * 10, i * 20, i));
entity.addComponent(new PerfHealthComponent(100 + i));
originalEntities.push(entity);
}
// 创建快照
const snapshotStart = performance.now();
const snapshot = snapshotManager.createSceneSnapshot(originalEntities);
const snapshotTime = performance.now() - snapshotStart;
// 创建目标实体
const targetEntities: Entity[] = [];
for (let i = 0; i < entityCount; i++) {
const entity = scene.createEntity(`Target${i}`);
entity.addComponent(new PerfPositionComponent());
entity.addComponent(new PerfHealthComponent());
targetEntities.push(entity);
}
// 测试恢复性能
const restoreStart = performance.now();
snapshotManager.restoreFromSnapshot(snapshot, targetEntities);
const restoreTime = performance.now() - restoreStart;
console.log(`\\n=== 反序列化性能测试 ===`);
console.log(`实体数量: ${entityCount}`);
console.log(`序列化时间: ${snapshotTime.toFixed(2)}ms`);
console.log(`反序列化时间: ${restoreTime.toFixed(2)}ms`);
console.log(`总往返时间: ${(snapshotTime + restoreTime).toFixed(2)}ms`);
console.log(`平均每实体往返时间: ${((snapshotTime + restoreTime) / entityCount).toFixed(3)}ms`);
expect(restoreTime).toBeLessThan(2000); // 不应该超过2秒
expect(snapshotTime + restoreTime).toBeLessThan(3000); // 总时间不超过3秒
});
});
describe('内存使用', () => {
it('应该监控序列化过程中的内存使用', () => {
const entityCount = 200;
const entities: Entity[] = [];
// 创建大量实体
for (let i = 0; i < entityCount; i++) {
const entity = scene.createEntity(`MemoryTest${i}`);
entity.addComponent(new PerfPositionComponent(
Math.random() * 1000,
Math.random() * 1000,
Math.random() * 100
));
entity.addComponent(new PerfVelocityComponent(
Math.random() * 10,
Math.random() * 10,
Math.random() * 2
));
entity.addComponent(new PerfHealthComponent(Math.floor(Math.random() * 200) + 50));
entities.push(entity);
}
// 记录初始内存(如果可用)
const initialMemory = (performance as any).memory?.usedJSHeapSize || 0;
// 执行序列化
const snapshot = snapshotManager.createSceneSnapshot(entities);
// 记录序列化后内存
const afterMemory = (performance as any).memory?.usedJSHeapSize || 0;
const memoryIncrease = afterMemory - initialMemory;
if (initialMemory > 0) {
console.log(`\\n=== 内存使用测试 ===`);
console.log(`实体数量: ${entityCount}`);
console.log(`初始内存: ${(initialMemory / 1024 / 1024).toFixed(2)} MB`);
console.log(`序列化后内存: ${(afterMemory / 1024 / 1024).toFixed(2)} MB`);
console.log(`内存增加: ${(memoryIncrease / 1024).toFixed(2)} KB`);
console.log(`平均每实体内存: ${(memoryIncrease / entityCount).toFixed(1)} bytes`);
}
expect(snapshot.entities).toHaveLength(entityCount);
// 清理
entities.length = 0;
});
});
describe('极端情况性能', () => {
it('应该处理大量小组件的性能', () => {
const componentCount = 5000;
const components: PerfPositionComponent[] = [];
// 创建大量小组件
for (let i = 0; i < componentCount; i++) {
components.push(new PerfPositionComponent(i, i * 2, i * 3));
}
const start = performance.now();
for (const component of components) {
protobufSerializer.serialize(component);
}
const time = performance.now() - start;
console.log(`\\n=== 大量小组件性能测试 ===`);
console.log(`组件数量: ${componentCount}`);
console.log(`总时间: ${time.toFixed(2)}ms`);
console.log(`平均每组件: ${(time / componentCount).toFixed(4)}ms`);
console.log(`每秒处理: ${Math.floor(componentCount / (time / 1000))} 个组件`);
expect(time).toBeLessThan(10000); // 不超过10秒
expect(time / componentCount).toBeLessThan(2); // 每个组件不超过2ms
});
});
});