import { Arbiter } from '../src/core/Arbiter.js'; function parseArgs(argv) { const args = new Map(); for (let i = 2; i < argv.length; i++) { const value = argv[i]; if (!value.startsWith('--')) continue; const [key, inline] = value.slice(2).split('='); if (inline !== undefined) { args.set(key, inline); continue; } const next = argv[i + 1]; if (next && !next.startsWith('--')) { args.set(key, next); i++; } else { args.set(key, true); } } return args; } function parseSizes(value) { if (!value) return null; return value.split(',').map(item => Number(item.trim())).filter(Number.isFinite); } function formatBytes(bytes) { const units = ['B', 'KB', 'MB', 'GB']; let idx = 0; let val = bytes; while (val >= 1024 && idx < units.length - 1) { val /= 1024; idx++; } return `${val.toFixed(2)}${units[idx]}`; } function gc() { if (global.gc) { global.gc(); global.gc(); } } function measureMemory(label) { const mem = process.memoryUsage(); return { label, rss: mem.rss, heapUsed: mem.heapUsed, heapTotal: mem.heapTotal, external: mem.external }; } function buildNodes(arbiter, size) { for (let i = 0; i < size; i++) { arbiter.addNode(`user:${i}`, 'user'); arbiter.addNode(`resource:${i}`, 'resource'); } } function addEdges(arbiter, size, relation, perNode, srcPrefix = 'user', dstPrefix = 'resource') { for (let i = 0; i < size; i++) { for (let j = 0; j < perNode; j++) { const dstId = (i + j) % size; arbiter.addRelation(`${srcPrefix}:${i}`, relation, `${dstPrefix}:${dstId}`); } } } function buildGraph(arbiter, size) { buildNodes(arbiter, size); addEdges(arbiter, size, 'owner', 1); addEdges(arbiter, size, 'viewer', 1); addEdges(arbiter, size, 'group_tuple_to_userset', 1); addEdges(arbiter, size, 'plan_chain', 1); addEdges(arbiter, size, 'account_admin_chain', 1); addEdges(arbiter, size, 'blocked_unless', 1); addEdges(arbiter, size, 'age_abac', 2); addEdges(arbiter, size, 'risk_limit', 2); addEdges(arbiter, size, 'feature_flag', 1); addEdges(arbiter, size, 'owa_union', 1); addEdges(arbiter, size, 'owa_comparator_nested', 5); arbiter.setRelationConfig('owner', { type: 'direct' }); arbiter.setRelationConfig('viewer', { type: 'direct' }); arbiter.setRelationConfig('group_tuple_to_userset', { type: 'direct' }); arbiter.setRelationConfig('plan_chain', { type: 'direct' }); arbiter.setRelationConfig('account_admin_chain', { type: 'direct' }); arbiter.setRelationConfig('blocked_unless', { type: 'direct' }); arbiter.setRelationConfig('age_abac', { type: 'direct' }); arbiter.setRelationConfig('risk_limit', { type: 'direct' }); arbiter.setRelationConfig('feature_flag', { type: 'direct' }); arbiter.setRelationConfig('owa_union', { type: 'direct' }); arbiter.setRelationConfig('owa_comparator_nested', { type: 'direct' }); } const args = parseArgs(process.argv); const sizes = parseSizes(args.get('sizes')) || [10000, 50000, 100000]; console.log('memory_profile'); console.log('size,nodes,edges,rss,heap_used,heap_total,external'); for (const size of sizes) { const arbiter = new Arbiter(); gc(); const before = measureMemory('before'); buildGraph(arbiter, size); gc(); const after = measureMemory('after'); const nodes = size * 2; const edges = size * (1 + 1 + 1 + 1 + 1 + 1 + 2 + 2 + 1 + 1 + 5); const delta = { rss: after.rss - before.rss, heapUsed: after.heapUsed - before.heapUsed, heapTotal: after.heapTotal - before.heapTotal, external: after.external - before.external }; console.log([ size, nodes, edges, formatBytes(delta.rss), formatBytes(delta.heapUsed), formatBytes(delta.heapTotal), formatBytes(delta.external) ].join(',')); }