#!/usr/bin/env node
/**
* bench.js — Benchmarks for the AtomSpace knowledge layer
*
* Measures:
* 1. CogServer connection latency
* 2. Single eval round-trip
* 3. Ingest throughput (atoms/sec)
* 4. Query latency (blastRadius, similarLoops, riskAssessment)
* 5. MeetLink vs incoming-set traversal
* 6. Graph size and composition
* 7. Preflight hook end-to-end latency
*
* Usage: node atomspace/test/bench.js
* Requires: CogServer running on port 17001
*/
const { isCogServerUp, evalScheme } = require('../cogserver')
const { blastRadius, similarLoops, riskAssessment } = require('../query')
const { ingestCodetree, treeToScheme, scanCodetree } = require('../ingest-codetree')
const { ingestRatchet } = require('../ingest-ratchet')
const { execSync } = require('child_process')
const path = require('path')
async function timeIt (name, fn, iterations = 1) {
const times = []
for (let i = 0; i < iterations; i++) {
const start = Date.now()
await fn()
times.push(Date.now() - start)
}
times.sort((a, b) => a - b)
const avg = times.reduce((a, b) => a + b, 0) / times.length
const min = times[0]
const max = times[times.length - 1]
const median = times[Math.floor(times.length / 2)]
const rate = iterations > 1 ? ` (${(1000 / avg).toFixed(0)} ops/sec)` : ''
console.log(` ${name.padEnd(40)} avg=${avg.toFixed(0)}ms min=${min}ms max=${max}ms median=${median}ms${rate}`)
return { avg, min, max, median }
}
async function main () {
console.log('AtomSpace Knowledge Layer — Benchmarks')
console.log('=' .repeat(60))
const up = await isCogServerUp()
if (!up) {
console.log('\nCogServer not running. Start it first: cogserver &')
process.exit(1)
}
// ── 1. Connection ──
console.log('\n── Connection ──')
await timeIt('isCogServerUp() probe', () => isCogServerUp(), 20)
await timeIt('evalScheme("(+ 1 1)") round-trip', () => evalScheme('(+ 1 1)'), 20)
// ── 2. Graph size ──
console.log('\n── Graph Size ──')
const counts = await evalScheme('(cog-report-counts)')
console.log(' ' + counts)
const edgeCount = await evalScheme('(length (cog-get-atoms (quote EdgeLink)))')
const conceptCount = await evalScheme('(length (cog-get-atoms (quote ConceptNode)))')
console.log(` ConceptNodes: ${conceptCount} EdgeLinks: ${edgeCount}`)
const bonds = ['contains', 'imports', 'depends-on', 'exports', 'loop-in-file', 'rule-for-file']
for (const b of bonds) {
const c = await evalScheme(`(length (cog-incoming-by-type (BondNode "${b}") (quote EdgeLink)))`)
console.log(` ${b}: ${c} edges`)
}
// ── 3. Query latency ──
console.log('\n── Query Latency ──')
const testFiles = [
'hyper-overlay/index.js',
'hypernote/lib/auth.js',
'pear-git-v3/lib/pear-git.js',
'pear-git/pear-git-server.js',
'codetree/lib/parser.js'
]
for (const f of testFiles) {
await timeIt(`blastRadius(${f.split('/').pop()})`,
() => blastRadius(f), 5)
}
await timeIt('similarLoops(3 files)',
() => similarLoops(['hyper-overlay/index.js', 'hypernote/lib/auth.js', 'pear-git/pear-git-server.js']), 5)
await timeIt('riskAssessment(3 files)',
() => riskAssessment(['hyper-overlay/index.js', 'pear-git-v3/lib/pear-git.js', 'hypernote/lib/auth.js']), 5)
// ── 4. Raw MeetLink latency ──
console.log('\n── Raw MeetLink vs Incoming-Set ──')
await timeIt('MeetLink: children of mod:hypernote',
() => evalScheme('(cog-execute! (Meet (TypedVariable (Variable "$f") (Type (quote ConceptNode))) (Present (EdgeLink (BondNode "contains") (ListLink (ConceptNode "mod:hypernote") (Variable "$f"))))))'), 10)
await timeIt('incoming-set: edges of BondNode',
() => evalScheme('(length (cog-incoming-by-type (BondNode "contains") (quote EdgeLink)))'), 10)
await timeIt('incoming-set: ListLinks for a file',
() => evalScheme('(length (cog-incoming-by-type (ConceptNode "file:hyper-overlay/index.js") (quote ListLink)))'), 10)
// ── 5. Ingest throughput ──
console.log('\n── Ingest Throughput ──')
// Codetree scan (no ingest, just measure scan speed)
const scanResult = await timeIt('codetree scan (JSON generation)',
() => { scanCodetree(); return true }, 1)
const tree = scanCodetree()
const exprs = treeToScheme(tree)
console.log(` Codetree: ${exprs.length} Scheme expressions from ${Object.keys(tree.modules || {}).length} modules`)
// Measure batch ingest speed
const batchSizes = [10, 50, 100]
for (const size of batchSizes) {
const batch = exprs.slice(0, size)
await timeIt(`ingest batch of ${size} expressions`,
() => evalScheme('(begin ' + batch.join(' ') + ')'), 3)
}
// Full ingest
const fullStart = Date.now()
await ingestRatchet()
const ratchetMs = Date.now() - fullStart
console.log(` Full ratchet ingest: ${ratchetMs}ms`)
// ── 6. Preflight hook end-to-end ──
console.log('\n── Preflight Hook End-to-End ──')
const hookTestFiles = [
'/storage/projects/code/hypercore-kb/hyper-fleeky/hyper-overlay/index.js',
'/storage/projects/code/hypercore-kb/hyper-fleeky/hypernote/lib/auth.js',
'/storage/projects/code/hypercore-kb/hyper-fleeky/codetree/lib/parser.js'
]
for (const f of hookTestFiles) {
// Clear state so it doesn't skip
try { require('fs').unlinkSync('/tmp/claude-preflight-state.json') } catch {}
const label = f.split('/').pop()
await timeIt(`preflight hook (${label})`, async () => {
const input = JSON.stringify({ tool_name: 'Edit', tool_input: { file_path: f, old_string: 'x', new_string: 'y' } })
try {
execSync(`echo '${input}' | node ${path.resolve(__dirname, '../../preflight-review.js')}`, {
timeout: 5000, stdio: 'pipe'
})
} catch {}
}, 3)
}
// ── Summary ──
console.log('\n' + '=' .repeat(60))
console.log('Benchmark complete.')
}
main().catch(err => {
console.error('Bench failed:', err.message)
process.exit(1)
})