Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions db-service/bench/cases/assoc-to-join-recursive.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
'use strict'

const cds = require('@sap/cds')

module.exports = {
name: 'assoc-to-join: recursive depth 3',
buildInput: () => cds.ql`SELECT from my.Genres { ID, parent.parent.parent.name }`,
}
8 changes: 8 additions & 0 deletions db-service/bench/cases/assoc-to-join-simple.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
'use strict'

const cds = require('@sap/cds')

module.exports = {
name: 'assoc-to-join: simple',
buildInput: () => cds.ql`SELECT from my.Books { ID, author.name }`,
}
8 changes: 8 additions & 0 deletions db-service/bench/cases/exists-recursive.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
'use strict'

const cds = require('@sap/cds')

module.exports = {
name: 'exists: recursive depth 3',
buildInput: () => cds.ql`SELECT from my.Genres { ID } where exists parent.parent.parent`,
}
8 changes: 8 additions & 0 deletions db-service/bench/cases/exists-simple.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
'use strict'

const cds = require('@sap/cds')

module.exports = {
name: 'exists: simple',
buildInput: () => cds.ql`SELECT from my.Books { ID } where exists author`,
}
8 changes: 8 additions & 0 deletions db-service/bench/cases/expand-recursive.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
'use strict'

const cds = require('@sap/cds')

module.exports = {
name: 'expand: recursive depth 3',
buildInput: () => cds.ql`SELECT from my.Genres { ID, parent { parent { parent { name }}} }`,
}
8 changes: 8 additions & 0 deletions db-service/bench/cases/expand-simple.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
'use strict'

const cds = require('@sap/cds')

module.exports = {
name: 'expand: simple',
buildInput: () => cds.ql`SELECT from my.Authors { ID, books { title } }`,
}
8 changes: 8 additions & 0 deletions db-service/bench/cases/simple-select.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
'use strict'

const cds = require('@sap/cds')

module.exports = {
name: 'Most Basic Select',
buildInput: () => cds.ql`SELECT from my.Books { ID }`,
}
24 changes: 24 additions & 0 deletions db-service/bench/model/schema.cds
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
namespace my;

entity Books {
key ID : Integer;
title : String;
stock : Integer;
author : Association to Authors;
genre : Association to Genres;
}

entity Authors {
key ID : Integer;
name : String;
books : Association to many Books
on books.author = $self;
}

entity Genres {
key ID : Integer;
name : String;
parent : Association to Genres;
children : Composition of many Genres
on children.parent = $self;
}
8 changes: 8 additions & 0 deletions db-service/bench/results/results.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
commit_sha,case,n,min_ms,max_ms,median_ms,p95_ms,mean_ms,mad_ms
681dc75033f5,Most Basic Select,500,0.019,2.964,0.021,0.070,0.040,0.001
681dc75033f5,exists: simple,500,0.026,1.038,0.030,0.051,0.034,0.002
681dc75033f5,assoc-to-join: simple,500,0.034,1.379,0.039,0.075,0.048,0.003
681dc75033f5,exists: recursive depth 3,500,0.035,0.791,0.040,0.058,0.045,0.003
681dc75033f5,assoc-to-join: recursive depth 3,500,0.036,0.631,0.046,0.072,0.050,0.003
681dc75033f5,expand: simple,500,0.060,2.042,0.073,0.116,0.086,0.004
681dc75033f5,expand: recursive depth 3,500,0.129,2.593,0.162,0.571,0.224,0.015
158 changes: 158 additions & 0 deletions db-service/bench/runner.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
'use strict'

const { performance } = require('node:perf_hooks')
const fs = require('node:fs')
const path = require('node:path')
const { execSync } = require('node:child_process')

const cds = require('@sap/cds')
const cqn4sql = require('../lib/cqn4sql')

// config
const ITER = Number(process.env.ITER || 500)
const WARMUP = Number(process.env.WARMUP || 50)
const CSV_PATH = process.env.CSV || path.join(__dirname, 'results/results.csv')

// stats in ms
function stats(ms) {
// sort values ascending for percentile/median calculation
ms.sort((a, b) => a - b)

const n = ms.length

const min = ms[0]
const max = ms[n - 1]
const median = ms[Math.floor(n / 2)]
const mean = ms.reduce((s, x) => s + x, 0) / n

// 95th percentile --> 5% of runs were slower than p95
const p95 = ms[Math.floor(n * 0.95)]
// MAD (Median Absolute Deviation): measure of variability
const madArr = ms.map(x => Math.abs(x - median)).sort((a, b) => a - b)
const mad = madArr[Math.floor(n / 2)]

return { n, min, max, median, p95, mean, mad }
}

function getCommitSha() {
try {
return execSync('git rev-parse --short=12 HEAD', { cwd: path.join(__dirname, '..') })
.toString()
.trim()
} catch {
return 'unknown'
}
}

function fmtNum(x) {
return x.toFixed(3)
}

function appendCsvRow(filePath, commit, row) {
const line =
[
commit,
row.case,
row.n,
fmtNum(row.min),
fmtNum(row.max),
fmtNum(row.median),
fmtNum(row.p95),
fmtNum(row.mean),
fmtNum(row.mad),
].join(',') + '\n'
fs.appendFileSync(filePath, line, 'utf8')
}

function ensureCsvHeader(filePath) {
const header = 'commit_sha,case,n,min_ms,max_ms,median_ms,p95_ms,mean_ms,mad_ms\n'
if (!fs.existsSync(filePath) || fs.statSync(filePath).size === 0) {
fs.mkdirSync(path.dirname(filePath), { recursive: true })
fs.writeFileSync(filePath, header, 'utf8')
}
}

function removeExistingEntriesForCommit(filePath, commit) {
if (!fs.existsSync(filePath)) return
const prefix = commit + ','
const lines = fs.readFileSync(filePath, 'utf8').split(/\r?\n/)

const filtered = lines.filter((line, i) => i === 0 || !line.startsWith(prefix))

fs.writeFileSync(filePath, filtered.join('\n'), 'utf8')
}

function gcSafe() {
if (global.gc) global.gc()
}

;(async function run() {
const modelDir = path.join(__dirname, 'model')
const model = await cds.load([modelDir]).then(cds.linked)

// discover cases
const casesDir = path.join(__dirname, 'cases')
const caseFiles = fs.readdirSync(casesDir).filter(f => /\.(cjs|js)$/.test(f))

const commit = getCommitSha()
// prepare CSV
ensureCsvHeader(CSV_PATH)
removeExistingEntriesForCommit(CSV_PATH, commit)

const results = []

for (const f of caseFiles) {
const mod = require(path.join(casesDir, f))
const label = mod.name || path.basename(f, path.extname(f))

if (typeof mod.setup === 'function') await mod.setup({ cqn4sql, model, cds })

// warmup
for (let i = 0; i < WARMUP; i++) {
const cqn = mod.buildInput({ model, cds })
cqn4sql(cqn, model)
}
gcSafe()

// timed iterations
const times = []
for (let i = 0; i < ITER; i++) {
const cqn = mod.buildInput({ model, cds })
const t0 = performance.now()
cqn4sql(cqn, model)
const t1 = performance.now()
times.push(t1 - t0)
}

if (typeof mod.teardown === 'function') await mod.teardown()

const s = stats(times)
const row = { case: label, ...s }
results.push({ commit, ...row })
gcSafe()
}

results.sort((a, b) => a.median - b.median)

console.log('case,n,min_ms,max_ms,median_ms,p95_ms,mean_ms,mad_ms')

Check warning on line 137 in db-service/bench/runner.cjs

View workflow job for this annotation

GitHub Actions / Tests (22)

Unexpected console statement
for (const row of results) {
console.log(

Check warning on line 139 in db-service/bench/runner.cjs

View workflow job for this annotation

GitHub Actions / Tests (22)

Unexpected console statement
[
row.case,
row.n,
fmtNum(row.min),
fmtNum(row.max),
fmtNum(row.median),
fmtNum(row.p95),
fmtNum(row.mean),
fmtNum(row.mad),
].join(','),
)
appendCsvRow(CSV_PATH, commit, row)
}

console.error(`\nAppended results to ${CSV_PATH} (commit ${commit})`)

Check warning on line 154 in db-service/bench/runner.cjs

View workflow job for this annotation

GitHub Actions / Tests (22)

Unexpected console statement
})().catch(err => {
console.error(err)

Check warning on line 156 in db-service/bench/runner.cjs

View workflow job for this annotation

GitHub Actions / Tests (22)

Unexpected console statement
process.exit(1)
})
Loading