Files
core/tests/engine/relation-store.test.js
T

219 lines
7.1 KiB
JavaScript
Raw Normal View History

import assert from 'node:assert/strict';
import { describe, test } from 'node:test';
import { RelationStore } from '../../src/core/RelationStore.js';
const runPerf = process.env.RUN_PERF_TESTS === '1';
const perfTest = runPerf ? test : test.skip;
describe.skip('RelationStore (SoA Memory Optimization)', () => {
test('basic add and retrieve', () => {
const store = new RelationStore();
const idx1 = store.add(1, 'owner', 2, { possibility: 1.0 });
const idx2 = store.add(1, 'member', 3, { possibility: 0.5 });
assert.strictEqual(store.size(), 2);
const rel1 = store.get(idx1);
assert.strictEqual(rel1.rel, 'owner');
assert.ok(Math.abs(rel1.possibility - 1.0) < 1e-6);
const rel2 = store.get(idx2);
assert.strictEqual(rel2.rel, 'member');
assert.ok(Math.abs(rel2.possibility - 0.5) < 1e-6);
});
test('update relation', () => {
const store = new RelationStore();
const idx = store.add(1, 'owner', 2, { possibility: 0.5 });
assert.strictEqual(store.possibility[idx], 0.5);
store.update(idx, { possibility: 0.9 });
assert.ok(Math.abs(store.possibility[idx] - 0.9) < 1e-6, `possibility should be ~0.9, got ${store.possibility[idx]}`);
const rel = store.get(idx);
assert.ok(Math.abs(rel.possibility - 0.9) < 1e-6);
});
test('remove relation', () => {
const store = new RelationStore();
const idx1 = store.add(1, 'owner', 2);
const idx2 = store.add(1, 'member', 3);
const idx3 = store.add(2, 'owner', 4);
assert.strictEqual(store.size(), 3);
store.remove(idx2);
assert.strictEqual(store.size(), 2);
const rel1 = store.get(idx1);
assert.strictEqual(rel1.rel, 'owner');
// After removal, idx2's data was moved from idx3
const rel3 = store.get(idx3);
assert.strictEqual(rel3, null);
const remaining = store.findMatches(null, null, null);
assert.strictEqual(remaining.length, 2);
});
test('find matches', () => {
const store = new RelationStore();
store.add(1, 'owner', 2);
store.add(1, 'member', 3);
store.add(1, 'owner', 4);
store.add(2, 'owner', 3);
const ownerFrom1 = store.findMatches(1, store.getRelationId('owner'), null);
assert.strictEqual(ownerFrom1.length, 2);
const allOwner = store.findMatches(null, store.getRelationId('owner'), null);
assert.strictEqual(allOwner.length, 3);
});
test('raw access for performance', () => {
const store = new RelationStore();
const idx = store.add(1, 'owner', 2, { possibility: 0.75 });
const raw = store.getRaw(idx);
assert.ok(raw !== null, 'raw should not be null');
assert.strictEqual(raw.src, 1);
const relId = store.getRelationId('owner');
assert.strictEqual(raw.relId, relId);
assert.strictEqual(raw.dst, 2);
assert.ok(Math.abs(raw.possibility - 0.75) < 1e-6);
assert.strictEqual(raw.reliability, 1.0);
assert.strictEqual('value' in raw, true);
});
test('metadata storage', () => {
const store = new RelationStore();
const customValue = { tier: 'premium', level: 5 };
const decay = { rate: 0.1, interval: 3600000 };
const idx = store.add(1, 'owner', 2, {
possibility: 1.0,
value: customValue,
decayConfig: decay,
stateId: 'test-state-123'
});
const rel = store.get(idx);
assert.deepStrictEqual(rel.value, customValue);
assert.deepStrictEqual(rel.decayConfig, decay);
assert.strictEqual(rel.stateId, 'test-state-123');
});
test('capacity expansion', () => {
const store = new RelationStore(4);
assert.strictEqual(store.capacity(), 4);
for (let i = 0; i < 10; i++) {
store.add(i, 'rel', i + 1);
}
assert.strictEqual(store.size(), 10);
assert.ok(store.capacity() >= 10);
});
test('forEach iteration', () => {
const store = new RelationStore();
store.add(1, 'owner', 2);
store.add(1, 'member', 3);
store.add(2, 'owner', 4);
let count = 0;
store.forEach((rel) => {
count++;
assert.ok(rel.src >= 1);
assert.ok(rel.dst >= 2);
});
assert.strictEqual(count, 3);
});
perfTest('memory efficiency compared to AoS', () => {
const numRelations = 100000;
const store = new RelationStore(numRelations);
const startMem = process.memoryUsage().heapUsed;
for (let i = 0; i < numRelations; i++) {
store.add(i % 1000, 'relation', (i + 1) % 1000, {
possibility: Math.random(),
reliability: Math.random()
});
}
const endMem = process.memoryUsage().heapUsed;
const memDelta = endMem - startMem;
const memMB = memDelta / (1024 * 1024);
const stats = store.getStats();
console.log(`RelationStore memory for ${numRelations} relations:`);
console.log(` - Typed arrays: ${(stats.memoryUsage.typedArrays / 1024 / 1024).toFixed(2)} MB`);
console.log(` - Metadata: ${(stats.memoryUsage.metadata / 1024 / 1024).toFixed(2)} MB`);
console.log(` - Total heap delta: ${memMB.toFixed(2)} MB`);
console.log(` - Bytes per relation: ${(memDelta / numRelations).toFixed(2)}`);
console.log(` - Capacity: ${stats.capacity}, Utilization: ${(stats.size / stats.capacity).toFixed(2)}`);
assert.strictEqual(store.size(), numRelations);
assert.ok(memMB < 50, `Memory usage should be efficient (< 50MB), got ${memMB.toFixed(2)} MB`);
});
perfTest('comparison with traditional object array', () => {
const numRelations = 50000;
const start1 = process.memoryUsage().heapUsed;
const traditional = [];
for (let i = 0; i < numRelations; i++) {
traditional.push({
src: i % 1000,
rel: 'relation',
dst: (i + 1) % 1000,
possibility: Math.random(),
reliability: Math.random(),
updated_last_at: Date.now(),
changed_last_at: Date.now(),
stateId: 'state-' + i
});
}
const end1 = process.memoryUsage().heapUsed;
const traditionalMem = end1 - start1;
const start2 = process.memoryUsage().heapUsed;
const store = new RelationStore(numRelations);
for (let i = 0; i < numRelations; i++) {
store.add(i % 1000, 'relation', (i + 1) % 1000, {
possibility: Math.random(),
reliability: Math.random()
});
}
const end2 = process.memoryUsage().heapUsed;
const soaMem = end2 - start2;
const savingsMB = (traditionalMem - soaMem) / (1024 * 1024);
const savingsPercent = ((traditionalMem - soaMem) / traditionalMem * 100);
console.log(`Memory comparison for ${numRelations} relations:`);
console.log(` - Traditional AoS: ${(traditionalMem / 1024 / 1024).toFixed(2)} MB`);
console.log(` - SoA RelationStore: ${(soaMem / 1024 / 1024).toFixed(2)} MB`);
console.log(` - Savings: ${savingsMB.toFixed(2)} MB (${savingsPercent.toFixed(1)}%)`);
console.log(` - Bytes per relation (AoS): ${(traditionalMem / numRelations).toFixed(2)}`);
console.log(` - Bytes per relation (SoA): ${(soaMem / numRelations).toFixed(2)}`);
assert.ok(soaMem <= traditionalMem, 'SoA should use less memory than AoS');
});
});