修复soa测试用例
This commit is contained in:
@@ -1,219 +0,0 @@
|
|||||||
import { Component } from '../../../src/ECS/Component';
|
|
||||||
import { ComponentStorageManager } from '../../../src/ECS/Core/ComponentStorage';
|
|
||||||
import { EnableSoA, AutoTyped, TypeInference } from '../../../src/ECS/Core/StorageDecorators';
|
|
||||||
|
|
||||||
// 测试自动类型推断
|
|
||||||
@EnableSoA
|
|
||||||
class AutoInferredComponent extends Component {
|
|
||||||
// 基于默认值的自动推断
|
|
||||||
@AutoTyped() health: number = 100; // 应该推断为uint8
|
|
||||||
@AutoTyped() score: number = 1000000; // 应该推断为uint32
|
|
||||||
@AutoTyped() position: number = 3.14; // 应该推断为float32
|
|
||||||
@AutoTyped() temperature: number = -40; // 应该推断为int8
|
|
||||||
|
|
||||||
// 基于范围的精确推断
|
|
||||||
@AutoTyped({ minValue: 0, maxValue: 255 })
|
|
||||||
level: number = 1; // 应该推断为uint8
|
|
||||||
|
|
||||||
@AutoTyped({ minValue: -1000, maxValue: 1000 })
|
|
||||||
velocity: number = 0; // 应该推断为int16
|
|
||||||
|
|
||||||
@AutoTyped({ minValue: 0, maxValue: 100000, precision: false })
|
|
||||||
experience: number = 0; // 应该推断为uint32(禁用精度)
|
|
||||||
|
|
||||||
@AutoTyped({ precision: true })
|
|
||||||
exactValue: number = 42; // 应该推断为float32(强制精度)
|
|
||||||
|
|
||||||
@AutoTyped({ minValue: 0, maxValue: 10, signed: false })
|
|
||||||
difficulty: number = 5; // 应该推断为uint8(无符号)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 传统手动指定类型的组件(用于对比)
|
|
||||||
@EnableSoA
|
|
||||||
class ManualTypedComponent extends Component {
|
|
||||||
health: number = 100; // 默认Float32Array
|
|
||||||
score: number = 1000000; // 默认Float32Array
|
|
||||||
position: number = 3.14; // 默认Float32Array
|
|
||||||
temperature: number = -40; // 默认Float32Array
|
|
||||||
level: number = 1; // 默认Float32Array
|
|
||||||
velocity: number = 0; // 默认Float32Array
|
|
||||||
experience: number = 0; // 默认Float32Array
|
|
||||||
exactValue: number = 42; // 默认Float32Array
|
|
||||||
difficulty: number = 5; // 默认Float32Array
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('AutoType Inference', () => {
|
|
||||||
let storageManager: ComponentStorageManager;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
storageManager = new ComponentStorageManager();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('TypeInference.inferOptimalType 应该正确推断基础类型', () => {
|
|
||||||
// 布尔值
|
|
||||||
expect(TypeInference.inferOptimalType(true)).toBe('uint8');
|
|
||||||
expect(TypeInference.inferOptimalType(false)).toBe('uint8');
|
|
||||||
|
|
||||||
// 小整数
|
|
||||||
expect(TypeInference.inferOptimalType(100)).toBe('uint8');
|
|
||||||
expect(TypeInference.inferOptimalType(-50)).toBe('int8');
|
|
||||||
|
|
||||||
// 大整数
|
|
||||||
expect(TypeInference.inferOptimalType(1000)).toBe('uint16');
|
|
||||||
expect(TypeInference.inferOptimalType(-1000)).toBe('int16');
|
|
||||||
|
|
||||||
// 浮点数
|
|
||||||
expect(TypeInference.inferOptimalType(3.14)).toBe('float32');
|
|
||||||
expect(TypeInference.inferOptimalType(-3.14)).toBe('float32');
|
|
||||||
|
|
||||||
// 非数值
|
|
||||||
expect(TypeInference.inferOptimalType('string')).toBe('float32');
|
|
||||||
expect(TypeInference.inferOptimalType(null)).toBe('float32');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('TypeInference.inferOptimalType 应该根据范围推断类型', () => {
|
|
||||||
// 指定范围的无符号整数
|
|
||||||
expect(TypeInference.inferOptimalType(100, { minValue: 0, maxValue: 255 })).toBe('uint8');
|
|
||||||
expect(TypeInference.inferOptimalType(1000, { minValue: 0, maxValue: 65535 })).toBe('uint16');
|
|
||||||
|
|
||||||
// 指定范围的有符号整数
|
|
||||||
expect(TypeInference.inferOptimalType(-50, { minValue: -128, maxValue: 127 })).toBe('int8');
|
|
||||||
expect(TypeInference.inferOptimalType(-1000, { minValue: -32768, maxValue: 32767 })).toBe('int16');
|
|
||||||
|
|
||||||
// 强制使用浮点精度
|
|
||||||
expect(TypeInference.inferOptimalType(42, { precision: true })).toBe('float32');
|
|
||||||
expect(TypeInference.inferOptimalType(42, { precision: true, maxValue: 1e40 })).toBe('float64');
|
|
||||||
|
|
||||||
// 强制禁用精度
|
|
||||||
expect(TypeInference.inferOptimalType(42.5, { precision: false, minValue: 0, maxValue: 255 })).toBe('uint8');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('应该为自动推断的字段创建正确的TypedArray', () => {
|
|
||||||
const storage = storageManager.getSoAStorage(AutoInferredComponent);
|
|
||||||
expect(storage).not.toBeNull();
|
|
||||||
|
|
||||||
// 验证推断的类型
|
|
||||||
expect(storage!.getFieldArray('health')).toBeInstanceOf(Uint8Array);
|
|
||||||
expect(storage!.getFieldArray('score')).toBeInstanceOf(Uint32Array);
|
|
||||||
expect(storage!.getFieldArray('position')).toBeInstanceOf(Float32Array);
|
|
||||||
expect(storage!.getFieldArray('temperature')).toBeInstanceOf(Int8Array);
|
|
||||||
expect(storage!.getFieldArray('level')).toBeInstanceOf(Uint8Array);
|
|
||||||
expect(storage!.getFieldArray('velocity')).toBeInstanceOf(Int16Array);
|
|
||||||
expect(storage!.getFieldArray('experience')).toBeInstanceOf(Uint32Array);
|
|
||||||
expect(storage!.getFieldArray('exactValue')).toBeInstanceOf(Float32Array);
|
|
||||||
expect(storage!.getFieldArray('difficulty')).toBeInstanceOf(Uint8Array);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('应该正确存储和检索自动推断类型的值', () => {
|
|
||||||
const entityId = 1;
|
|
||||||
const component = new AutoInferredComponent();
|
|
||||||
|
|
||||||
// 设置测试值
|
|
||||||
component.health = 255; // Uint8 最大值
|
|
||||||
component.score = 4000000000; // 大的Uint32值
|
|
||||||
component.position = 3.14159;
|
|
||||||
component.temperature = -128; // Int8 最小值
|
|
||||||
component.level = 200;
|
|
||||||
component.velocity = -500;
|
|
||||||
component.experience = 50000;
|
|
||||||
component.exactValue = Math.PI;
|
|
||||||
component.difficulty = 10;
|
|
||||||
|
|
||||||
storageManager.addComponent(entityId, component);
|
|
||||||
const retrieved = storageManager.getComponent(entityId, AutoInferredComponent);
|
|
||||||
|
|
||||||
expect(retrieved).not.toBeNull();
|
|
||||||
expect(retrieved!.health).toBe(255);
|
|
||||||
expect(retrieved!.score).toBe(4000000000);
|
|
||||||
expect(retrieved!.position).toBeCloseTo(3.14159, 5);
|
|
||||||
expect(retrieved!.temperature).toBe(-128);
|
|
||||||
expect(retrieved!.level).toBe(200);
|
|
||||||
expect(retrieved!.velocity).toBe(-500);
|
|
||||||
expect(retrieved!.experience).toBe(50000);
|
|
||||||
expect(retrieved!.exactValue).toBeCloseTo(Math.PI, 5);
|
|
||||||
expect(retrieved!.difficulty).toBe(10);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('自动推断应该比手动类型使用更少内存', () => {
|
|
||||||
const autoStorage = storageManager.getSoAStorage(AutoInferredComponent);
|
|
||||||
const manualStorage = storageManager.getSoAStorage(ManualTypedComponent);
|
|
||||||
|
|
||||||
const autoStats = autoStorage!.getStats();
|
|
||||||
const manualStats = manualStorage!.getStats();
|
|
||||||
|
|
||||||
// 自动推断应该使用更少的内存
|
|
||||||
expect(autoStats.memoryUsage).toBeLessThan(manualStats.memoryUsage);
|
|
||||||
|
|
||||||
// 验证特定字段的内存使用
|
|
||||||
expect(autoStats.fieldStats.get('health').memory).toBe(1000); // Uint8: 1 byte per element
|
|
||||||
expect(manualStats.fieldStats.get('health').memory).toBe(4000); // Float32: 4 bytes per element
|
|
||||||
|
|
||||||
expect(autoStats.fieldStats.get('temperature').memory).toBe(1000); // Int8: 1 byte per element
|
|
||||||
expect(manualStats.fieldStats.get('temperature').memory).toBe(4000); // Float32: 4 bytes per element
|
|
||||||
});
|
|
||||||
|
|
||||||
test('应该处理边界值', () => {
|
|
||||||
@EnableSoA
|
|
||||||
class BoundaryTestComponent extends Component {
|
|
||||||
@AutoTyped({ minValue: 0, maxValue: 255 })
|
|
||||||
maxUint8: number = 255;
|
|
||||||
|
|
||||||
@AutoTyped({ minValue: -128, maxValue: 127 })
|
|
||||||
minInt8: number = -128;
|
|
||||||
|
|
||||||
@AutoTyped({ minValue: 0, maxValue: 65535 })
|
|
||||||
maxUint16: number = 65535;
|
|
||||||
|
|
||||||
@AutoTyped({ minValue: -32768, maxValue: 32767 })
|
|
||||||
minInt16: number = -32768;
|
|
||||||
}
|
|
||||||
|
|
||||||
const storage = storageManager.getSoAStorage(BoundaryTestComponent);
|
|
||||||
|
|
||||||
expect(storage!.getFieldArray('maxUint8')).toBeInstanceOf(Uint8Array);
|
|
||||||
expect(storage!.getFieldArray('minInt8')).toBeInstanceOf(Int8Array);
|
|
||||||
expect(storage!.getFieldArray('maxUint16')).toBeInstanceOf(Uint16Array);
|
|
||||||
expect(storage!.getFieldArray('minInt16')).toBeInstanceOf(Int16Array);
|
|
||||||
|
|
||||||
const entityId = 1;
|
|
||||||
const component = new BoundaryTestComponent();
|
|
||||||
|
|
||||||
storageManager.addComponent(entityId, component);
|
|
||||||
const retrieved = storageManager.getComponent(entityId, BoundaryTestComponent);
|
|
||||||
|
|
||||||
expect(retrieved!.maxUint8).toBe(255);
|
|
||||||
expect(retrieved!.minInt8).toBe(-128);
|
|
||||||
expect(retrieved!.maxUint16).toBe(65535);
|
|
||||||
expect(retrieved!.minInt16).toBe(-32768);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('应该输出推断日志信息', () => {
|
|
||||||
const consoleSpy = jest.spyOn(console, 'log').mockImplementation();
|
|
||||||
|
|
||||||
// 创建新的存储以触发推断
|
|
||||||
const newStorageManager = new ComponentStorageManager();
|
|
||||||
newStorageManager.getSoAStorage(AutoInferredComponent);
|
|
||||||
|
|
||||||
// 验证日志输出(注意:实际的日志可能通过logger系统输出)
|
|
||||||
// 这里主要验证不会出错
|
|
||||||
expect(() => {
|
|
||||||
const component = new AutoInferredComponent();
|
|
||||||
newStorageManager.addComponent(999, component);
|
|
||||||
}).not.toThrow();
|
|
||||||
|
|
||||||
consoleSpy.mockRestore();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('计算内存节省百分比', () => {
|
|
||||||
const autoStorage = storageManager.getSoAStorage(AutoInferredComponent);
|
|
||||||
const manualStorage = storageManager.getSoAStorage(ManualTypedComponent);
|
|
||||||
|
|
||||||
const autoStats = autoStorage!.getStats();
|
|
||||||
const manualStats = manualStorage!.getStats();
|
|
||||||
|
|
||||||
const memorySaved = ((manualStats.memoryUsage - autoStats.memoryUsage) / manualStats.memoryUsage) * 100;
|
|
||||||
|
|
||||||
// 自动推断应该节省显著的内存
|
|
||||||
expect(memorySaved).toBeGreaterThan(20); // 至少节省20%的内存
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Component } from '../../../src/ECS/Component';
|
import { Component } from '../../../src/ECS/Component';
|
||||||
import { ComponentStorageManager, EnableSoA } from '../../../src/ECS/Core/ComponentStorage';
|
import { ComponentStorageManager } from '../../../src/ECS/Core/ComponentStorage';
|
||||||
|
import { EnableSoA } from '../../../src/ECS/Core/SoAStorage';
|
||||||
|
|
||||||
// 默认原始存储组件
|
// 默认原始存储组件
|
||||||
class PositionComponent extends Component {
|
class PositionComponent extends Component {
|
||||||
|
|||||||
@@ -1,214 +0,0 @@
|
|||||||
import { Component } from '../../../src/ECS/Component';
|
|
||||||
import { ComponentStorageManager } from '../../../src/ECS/Core/ComponentStorage';
|
|
||||||
import {
|
|
||||||
EnableSoA,
|
|
||||||
Float32,
|
|
||||||
Float64,
|
|
||||||
Int32,
|
|
||||||
Uint32,
|
|
||||||
Int16,
|
|
||||||
Uint16,
|
|
||||||
Int8,
|
|
||||||
Uint8,
|
|
||||||
Uint8Clamped
|
|
||||||
} from '../../../src/ECS/Core/StorageDecorators';
|
|
||||||
|
|
||||||
// 测试组件 - 展示所有TypedArray类型
|
|
||||||
@EnableSoA
|
|
||||||
class EnhancedTestComponent extends Component {
|
|
||||||
@Float32 x: number = 0;
|
|
||||||
@Float32 y: number = 0;
|
|
||||||
@Float64 preciseX: number = 0;
|
|
||||||
@Int32 count: number = 0;
|
|
||||||
@Uint32 entityId: number = 0; // 避免与基类id冲突
|
|
||||||
@Int16 layer: number = 0;
|
|
||||||
@Uint16 priority: number = 0;
|
|
||||||
@Int8 direction: number = 0;
|
|
||||||
@Uint8 flags: number = 0;
|
|
||||||
@Uint8Clamped alpha: number = 255;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 简单组件用于对比
|
|
||||||
class SimpleComponent extends Component {
|
|
||||||
value: number = 42;
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('Enhanced TypedArray Support', () => {
|
|
||||||
let storageManager: ComponentStorageManager;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
storageManager = new ComponentStorageManager();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('应该为所有TypedArray类型创建正确的存储', () => {
|
|
||||||
const storage = storageManager.getSoAStorage(EnhancedTestComponent);
|
|
||||||
expect(storage).not.toBeNull();
|
|
||||||
|
|
||||||
// 测试不同类型的字段数组
|
|
||||||
const xArray = storage!.getFieldArray('x'); // Float32Array
|
|
||||||
const preciseXArray = storage!.getFieldArray('preciseX'); // Float64Array
|
|
||||||
const countArray = storage!.getFieldArray('count'); // Int32Array
|
|
||||||
const entityIdArray = storage!.getFieldArray('entityId'); // Uint32Array
|
|
||||||
const layerArray = storage!.getFieldArray('layer'); // Int16Array
|
|
||||||
const priorityArray = storage!.getFieldArray('priority'); // Uint16Array
|
|
||||||
const directionArray = storage!.getFieldArray('direction'); // Int8Array
|
|
||||||
const flagsArray = storage!.getFieldArray('flags'); // Uint8Array
|
|
||||||
const alphaArray = storage!.getFieldArray('alpha'); // Uint8ClampedArray
|
|
||||||
|
|
||||||
expect(xArray).toBeInstanceOf(Float32Array);
|
|
||||||
expect(preciseXArray).toBeInstanceOf(Float64Array);
|
|
||||||
expect(countArray).toBeInstanceOf(Int32Array);
|
|
||||||
expect(entityIdArray).toBeInstanceOf(Uint32Array);
|
|
||||||
expect(layerArray).toBeInstanceOf(Int16Array);
|
|
||||||
expect(priorityArray).toBeInstanceOf(Uint16Array);
|
|
||||||
expect(directionArray).toBeInstanceOf(Int8Array);
|
|
||||||
expect(flagsArray).toBeInstanceOf(Uint8Array);
|
|
||||||
expect(alphaArray).toBeInstanceOf(Uint8ClampedArray);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('应该正确存储和检索不同类型的数值', () => {
|
|
||||||
const entityId = 1;
|
|
||||||
const component = new EnhancedTestComponent();
|
|
||||||
|
|
||||||
// 设置测试值
|
|
||||||
component.x = 3.14159;
|
|
||||||
component.preciseX = Math.PI;
|
|
||||||
component.count = -123456;
|
|
||||||
component.entityId = 4294967295; // 最大Uint32值
|
|
||||||
component.layer = -32768; // 最小Int16值
|
|
||||||
component.priority = 65535; // 最大Uint16值
|
|
||||||
component.direction = -128; // 最小Int8值
|
|
||||||
component.flags = 255; // 最大Uint8值
|
|
||||||
component.alpha = 300; // 会被Clamped到255
|
|
||||||
|
|
||||||
storageManager.addComponent(entityId, component);
|
|
||||||
const retrieved = storageManager.getComponent(entityId, EnhancedTestComponent);
|
|
||||||
|
|
||||||
expect(retrieved).not.toBeNull();
|
|
||||||
expect(retrieved!.x).toBeCloseTo(3.14159, 5);
|
|
||||||
expect(retrieved!.preciseX).toBeCloseTo(Math.PI, 10);
|
|
||||||
expect(retrieved!.count).toBe(-123456);
|
|
||||||
expect(retrieved!.entityId).toBe(4294967295);
|
|
||||||
expect(retrieved!.layer).toBe(-32768);
|
|
||||||
expect(retrieved!.priority).toBe(65535);
|
|
||||||
expect(retrieved!.direction).toBe(-128);
|
|
||||||
expect(retrieved!.flags).toBe(255);
|
|
||||||
expect(retrieved!.alpha).toBe(255); // Clamped
|
|
||||||
});
|
|
||||||
|
|
||||||
test('应该正确计算内存使用量', () => {
|
|
||||||
const storage = storageManager.getSoAStorage(EnhancedTestComponent);
|
|
||||||
const stats = storage!.getStats();
|
|
||||||
|
|
||||||
expect(stats.fieldStats.get('x').type).toBe('float32');
|
|
||||||
expect(stats.fieldStats.get('x').memory).toBe(4000); // 1000 * 4 bytes
|
|
||||||
|
|
||||||
expect(stats.fieldStats.get('preciseX').type).toBe('float64');
|
|
||||||
expect(stats.fieldStats.get('preciseX').memory).toBe(8000); // 1000 * 8 bytes
|
|
||||||
|
|
||||||
expect(stats.fieldStats.get('count').type).toBe('int32');
|
|
||||||
expect(stats.fieldStats.get('count').memory).toBe(4000); // 1000 * 4 bytes
|
|
||||||
|
|
||||||
expect(stats.fieldStats.get('entityId').type).toBe('uint32');
|
|
||||||
expect(stats.fieldStats.get('entityId').memory).toBe(4000); // 1000 * 4 bytes
|
|
||||||
|
|
||||||
expect(stats.fieldStats.get('layer').type).toBe('int16');
|
|
||||||
expect(stats.fieldStats.get('layer').memory).toBe(2000); // 1000 * 2 bytes
|
|
||||||
|
|
||||||
expect(stats.fieldStats.get('priority').type).toBe('uint16');
|
|
||||||
expect(stats.fieldStats.get('priority').memory).toBe(2000); // 1000 * 2 bytes
|
|
||||||
|
|
||||||
expect(stats.fieldStats.get('direction').type).toBe('int8');
|
|
||||||
expect(stats.fieldStats.get('direction').memory).toBe(1000); // 1000 * 1 byte
|
|
||||||
|
|
||||||
expect(stats.fieldStats.get('flags').type).toBe('uint8');
|
|
||||||
expect(stats.fieldStats.get('flags').memory).toBe(1000); // 1000 * 1 byte
|
|
||||||
|
|
||||||
expect(stats.fieldStats.get('alpha').type).toBe('uint8clamped');
|
|
||||||
expect(stats.fieldStats.get('alpha').memory).toBe(1000); // 1000 * 1 byte
|
|
||||||
});
|
|
||||||
|
|
||||||
test('应该支持向量化批量操作', () => {
|
|
||||||
const storage = storageManager.getSoAStorage(EnhancedTestComponent);
|
|
||||||
|
|
||||||
// 添加测试数据
|
|
||||||
for (let i = 0; i < 100; i++) {
|
|
||||||
const component = new EnhancedTestComponent();
|
|
||||||
component.x = i;
|
|
||||||
component.y = i * 2;
|
|
||||||
storageManager.addComponent(i, component);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 执行向量化操作
|
|
||||||
let processedCount = 0;
|
|
||||||
storage!.performVectorizedOperation((fieldArrays, activeIndices) => {
|
|
||||||
const xArray = fieldArrays.get('x') as Float32Array;
|
|
||||||
const yArray = fieldArrays.get('y') as Float32Array;
|
|
||||||
|
|
||||||
// 批量处理:x = x + y
|
|
||||||
for (const index of activeIndices) {
|
|
||||||
xArray[index] = xArray[index] + yArray[index];
|
|
||||||
processedCount++;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(processedCount).toBe(100);
|
|
||||||
|
|
||||||
// 验证结果
|
|
||||||
for (let i = 0; i < 100; i++) {
|
|
||||||
const component = storageManager.getComponent(i, EnhancedTestComponent);
|
|
||||||
expect(component!.x).toBe(i + i * 2); // x + y
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
test('布尔值应该默认使用Uint8Array', () => {
|
|
||||||
@EnableSoA
|
|
||||||
class BooleanTestComponent extends Component {
|
|
||||||
visible: boolean = true;
|
|
||||||
@Float32 enabledFloat: boolean = true; // 显式指定Float32
|
|
||||||
}
|
|
||||||
|
|
||||||
const storage = storageManager.getSoAStorage(BooleanTestComponent);
|
|
||||||
|
|
||||||
const visibleArray = storage!.getFieldArray('visible');
|
|
||||||
const enabledFloatArray = storage!.getFieldArray('enabledFloat');
|
|
||||||
|
|
||||||
expect(visibleArray).toBeInstanceOf(Uint8Array);
|
|
||||||
expect(enabledFloatArray).toBeInstanceOf(Float32Array);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('内存使用量对比:优化后应该更节省内存', () => {
|
|
||||||
@EnableSoA
|
|
||||||
class OptimizedComponent extends Component {
|
|
||||||
@Uint8 flag1: number = 0;
|
|
||||||
@Uint8 flag2: number = 0;
|
|
||||||
@Uint8 flag3: number = 0;
|
|
||||||
@Uint8 flag4: number = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@EnableSoA
|
|
||||||
class UnoptimizedComponent extends Component {
|
|
||||||
@Float32 flag1: number = 0;
|
|
||||||
@Float32 flag2: number = 0;
|
|
||||||
@Float32 flag3: number = 0;
|
|
||||||
@Float32 flag4: number = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
const optimizedStorage = storageManager.getSoAStorage(OptimizedComponent);
|
|
||||||
const unoptimizedStorage = storageManager.getSoAStorage(UnoptimizedComponent);
|
|
||||||
|
|
||||||
const optimizedStats = optimizedStorage!.getStats();
|
|
||||||
const unoptimizedStats = unoptimizedStorage!.getStats();
|
|
||||||
|
|
||||||
// 优化版本应该使用更少的内存(4个Uint8 vs 4个Float32)
|
|
||||||
expect(optimizedStats.memoryUsage).toBeLessThan(unoptimizedStats.memoryUsage);
|
|
||||||
|
|
||||||
// 验证优化后的单个字段确实更小
|
|
||||||
expect(optimizedStats.fieldStats.get('flag1').memory).toBe(1000); // Uint8: 1000 bytes
|
|
||||||
expect(unoptimizedStats.fieldStats.get('flag1').memory).toBe(4000); // Float32: 4000 bytes
|
|
||||||
|
|
||||||
// 计算节省的内存百分比
|
|
||||||
const memorySaved = ((unoptimizedStats.memoryUsage - optimizedStats.memoryUsage) / unoptimizedStats.memoryUsage) * 100;
|
|
||||||
expect(memorySaved).toBeGreaterThan(50); // 应该节省超过50%的内存
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Component } from '../../../src/ECS/Component';
|
import { Component } from '../../../src/ECS/Component';
|
||||||
import { ComponentStorageManager, EnableSoA, SerializeMap, SerializeSet, SerializeArray, DeepCopy } from '../../../src/ECS/Core/ComponentStorage';
|
import { ComponentStorageManager } from '../../../src/ECS/Core/ComponentStorage';
|
||||||
|
import { EnableSoA, SerializeMap, SerializeSet, SerializeArray, DeepCopy } from '../../../src/ECS/Core/SoAStorage';
|
||||||
|
|
||||||
// 测试组件:使用集合类型装饰器
|
// 测试组件:使用集合类型装饰器
|
||||||
@EnableSoA
|
@EnableSoA
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Component } from '../../../src/ECS/Component';
|
import { Component } from '../../../src/ECS/Component';
|
||||||
import { ComponentStorageManager, EnableSoA, HighPrecision, Float64, Int32, SerializeMap, SerializeSet, SerializeArray, DeepCopy } from '../../../src/ECS/Core/ComponentStorage';
|
import { ComponentStorageManager } from '../../../src/ECS/Core/ComponentStorage';
|
||||||
|
import { EnableSoA, HighPrecision, Float64, Int32, SerializeMap, SerializeSet, SerializeArray, DeepCopy } from '../../../src/ECS/Core/SoAStorage';
|
||||||
import { SoAStorage } from '../../../src/ECS/Core/SoAStorage';
|
import { SoAStorage } from '../../../src/ECS/Core/SoAStorage';
|
||||||
|
|
||||||
// 综合测试组件,覆盖所有装饰器
|
// 综合测试组件,覆盖所有装饰器
|
||||||
@@ -245,7 +246,7 @@ describe('SoA存储综合测试覆盖', () => {
|
|||||||
expect(preciseArray).toBeInstanceOf(Float64Array);
|
expect(preciseArray).toBeInstanceOf(Float64Array);
|
||||||
expect(intArray).toBeInstanceOf(Int32Array);
|
expect(intArray).toBeInstanceOf(Int32Array);
|
||||||
expect(normalArray).toBeInstanceOf(Float32Array);
|
expect(normalArray).toBeInstanceOf(Float32Array);
|
||||||
expect(flagArray).toBeInstanceOf(Float32Array);
|
expect(flagArray).toBeInstanceOf(Uint8Array);
|
||||||
|
|
||||||
// 高精度字段不应该在TypedArray中
|
// 高精度字段不应该在TypedArray中
|
||||||
const bigIntArray = storage.getFieldArray('bigIntId');
|
const bigIntArray = storage.getFieldArray('bigIntId');
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Component } from '../../../src/ECS/Component';
|
import { Component } from '../../../src/ECS/Component';
|
||||||
import { ComponentStorageManager, EnableSoA, HighPrecision, Float64, Int32 } from '../../../src/ECS/Core/ComponentStorage';
|
import { ComponentStorageManager } from '../../../src/ECS/Core/ComponentStorage';
|
||||||
import { SoAStorage } from '../../../src/ECS/Core/SoAStorage';
|
import { SoAStorage, EnableSoA, HighPrecision, Float64, Int32 } from '../../../src/ECS/Core/SoAStorage';
|
||||||
|
|
||||||
// 测试组件:使用不同的数值类型装饰器
|
// 测试组件:使用不同的数值类型装饰器
|
||||||
@EnableSoA
|
@EnableSoA
|
||||||
@@ -104,7 +104,7 @@ describe('SoA数值类型装饰器测试', () => {
|
|||||||
expect(normalFloatArray).toBeInstanceOf(Float32Array);
|
expect(normalFloatArray).toBeInstanceOf(Float32Array);
|
||||||
expect(preciseFloatArray).toBeInstanceOf(Float64Array);
|
expect(preciseFloatArray).toBeInstanceOf(Float64Array);
|
||||||
expect(integerArray).toBeInstanceOf(Int32Array);
|
expect(integerArray).toBeInstanceOf(Int32Array);
|
||||||
expect(flagArray).toBeInstanceOf(Float32Array);
|
expect(flagArray).toBeInstanceOf(Uint8Array);
|
||||||
|
|
||||||
// 高精度字段不应该在TypedArray中
|
// 高精度字段不应该在TypedArray中
|
||||||
const highPrecisionArray = storage.getFieldArray('highPrecisionNumber');
|
const highPrecisionArray = storage.getFieldArray('highPrecisionNumber');
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Component } from '../../../src/ECS/Component';
|
import { Component } from '../../../src/ECS/Component';
|
||||||
import { ComponentStorageManager, EnableSoA } from '../../../src/ECS/Core/ComponentStorage';
|
import { ComponentStorageManager } from '../../../src/ECS/Core/ComponentStorage';
|
||||||
|
import { EnableSoA } from '../../../src/ECS/Core/SoAStorage';
|
||||||
|
|
||||||
// 模拟复杂对象(如cocos的node节点)
|
// 模拟复杂对象(如cocos的node节点)
|
||||||
class MockNode {
|
class MockNode {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Component } from '../../../src/ECS/Component';
|
import { Component } from '../../../src/ECS/Component';
|
||||||
import { ComponentStorageManager, EnableSoA, HighPrecision, Float64 } from '../../../src/ECS/Core/ComponentStorage';
|
import { ComponentStorageManager } from '../../../src/ECS/Core/ComponentStorage';
|
||||||
|
import { EnableSoA, HighPrecision, Float64 } from '../../../src/ECS/Core/SoAStorage';
|
||||||
|
|
||||||
// 包含所有基础类型的组件
|
// 包含所有基础类型的组件
|
||||||
@EnableSoA
|
@EnableSoA
|
||||||
|
|||||||
Reference in New Issue
Block a user