Skip to content

Commit

Permalink
Add iterate and clear benchmarks (#29)
Browse files Browse the repository at this point in the history
Undocumented for now because they're in POC stage.
  • Loading branch information
vweevers authored Sep 21, 2021
1 parent d36de97 commit d3797b0
Show file tree
Hide file tree
Showing 5 changed files with 381 additions and 0 deletions.
122 changes: 122 additions & 0 deletions benchmarks/clear.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
'use strict'

const keyspace = require('keyspace')
const window = 1000
const progressWindow = window * 100

exports.defaults = {
benchmark: {
n: 1e6,
valueSize: 100,
keys: 'seq',
values: 'random',
seed: 'seed'
}
}

exports.plot = require('./clear.plot')

exports.run = function (factory, stream, options) {
if (options.n < window) {
throw new RangeError('The "n" option must be >= ' + window)
} else if (options.n % window !== 0) {
throw new Error('The "n" option must be a multiple of ' + window)
}

const generator = keyspace(options.n, options)

stream.write('Elapsed (ms), Entries, Bytes, SMA ns/write, CMA entries/s\n')

function start (db) {
const startTime = Date.now()
const inProgress = 0 // TODO: remove
const totalBytes = 0 // TODO: remove. Can't know

let totalDeletes = 0
let timesAccum = 0
let elapsed

function report () {
console.log(
'Cleared', options.n, 'entries in',
Math.floor((Date.now() - startTime) / 1e3) + 's'
)

stream.end()

const it = db.iterator()

it.next(function (err, key) {
if (err) throw err
if (key !== undefined) throw new Error('Did not clear all')

it.end(function (err) {
if (err) throw err

db.close(function (err) {
if (err) throw err
})
})
})
}

function clear () {
if (totalDeletes >= options.n) return report(Date.now() - startTime)

const start = process.hrtime()
db.clear({ gte: generator.key(totalDeletes), limit: window }, function (err) {
if (err) throw err

const duration = process.hrtime(start)
const nano = (duration[0] * 1e9) + duration[1]

timesAccum += nano / window
totalDeletes += window

if (totalDeletes % progressWindow === 0) {
console.log('' + inProgress, totalDeletes,
Math.round(totalDeletes / options.n * 100) + '%')
}

elapsed = Date.now() - startTime
stream.write(
elapsed +
',' + totalDeletes +
',' + totalBytes +
',' + (timesAccum / window).toFixed(3) +
',' + ((totalDeletes) / (elapsed / 1e3)).toFixed(3) +
'\n')
timesAccum = 0
clear()
})
}

clear()
}

factory(function (err, db) {
if (err) throw err

let entries = 0

function loop (err) {
if (err) throw err

console.log('Prep: wrote %d of %d entries', entries, options.n)
if (entries >= options.n) return setTimeout(() => start(db), 500)

const batch = db.batch()

for (let i = 0; i < 1e3 && entries < options.n; i++) {
const key = generator.key(entries++)
const value = generator.value()

batch.put(key, value)
}

batch.write(loop)
}

loop()
})
}
55 changes: 55 additions & 0 deletions benchmarks/clear.plot.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
'use strict'

const e = require('../lib/escape-gnuplot-string')

module.exports = function (title, description, results) {
const durations = results.map(function (res, i) {
const file = res.csvFile
const title = res.id(results)

return `'${e(file)}' using ($1/1000):($4) title '${e(title)}' ls ${i + 1} axes x1y1`
})

const throughputs = results.map(function (res, i) {
const file = res.csvFile
const title = res.id(results)

return `'${e(file)}' using ($1/1000):($5) w lines title '${e(title)}' ls ${i + 1} axes x1y1`
})

return `
reset
set terminal pngcairo truecolor enhanced font "Ubuntu Mono,10" size 1920, 1080 background rgb "#1b1b1b"
set datafile separator ','
set autoscale y
set ytics mirror
set tics in
set xlabel "Time (seconds)" tc rgb "#999999"
set key outside tc rgb "#999999"
set border lc rgb "#999999"
# To plot more than 5 files, add more line styles
set style line 1 lt 7 ps 0.8 lc rgb "#00FFFF"
set style line 2 lt 7 ps 0.8 lc rgb "#D84797"
set style line 3 lt 7 ps 0.8 lc rgb "#23CE6B"
set style line 4 lt 7 ps 0.8 lc rgb "#F5B700"
set style line 5 lt 7 ps 0.8 lc rgb "#731DD8"
set multiplot layout 2,1
set lmargin at screen 0.1
set title '${e(title)}' tc rgb "#cccccc" offset 0,0.1 font "Ubuntu Mono,12"
set label 1 '${e(description)}' tc rgb "#999999" at graph 0.5,1.10 center front
set ylabel 'SMA ns/read' tc rgb "#999999"
set logscale y
plot ${durations.join(', ')}
set title ""
set label 1 ""
set ylabel 'CMA Throughput entries/s' tc rgb "#999999"
set nologscale y
plot ${throughputs.join(', ')}
unset multiplot`
}
2 changes: 2 additions & 0 deletions benchmarks/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@

exports.put = require('./put')
exports['batch-put'] = require('./batch-put')
exports.iterate = require('./iterate')
exports.clear = require('./clear')
exports['self-distribution'] = require('./self-distribution')
147 changes: 147 additions & 0 deletions benchmarks/iterate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
'use strict'

const keyspace = require('keyspace')
const ldu = require('../lib/level-du')
const window = 1000
const progressWindow = window * 100

exports.defaults = {
benchmark: {
n: 1e6,
concurrency: 1,
valueSize: 100,
buffers: false,
keys: 'random',
values: 'random',
seed: 'seed'
}
}

exports.plot = require('./iterate.plot')

exports.run = function (factory, stream, options) {
if (options.n < window) {
throw new RangeError('The "n" option must be >= ' + window)
} else if (options.n % window !== 0) {
throw new Error('The "n" option must be a multiple of ' + window)
}

const generator = keyspace(options.n, options)

stream.write('Elapsed (ms), Entries, Bytes, ns/read, CMA MB/s\n')

function start (db) {
const startTime = Date.now()

let inProgress = 0
let totalReads = 0
let totalBytes = 0
let timesAccum = 0
let elapsed

function report () {
console.log(
'Iterated', options.n, 'entries in',
Math.floor((Date.now() - startTime) / 1e3) + 's,',
(Math.floor((totalBytes / 1048576) * 100) / 100) + 'MB'
)

stream.end()

db.close(function (err) {
if (err) throw err

ldu(db, function (err, size) {
if (err) throw err
if (size) console.log('Database size:', Math.floor(size / 1024 / 1024) + 'M')
})
})
}

function iterate () {
if (totalReads >= options.n) return report(Date.now() - startTime)
if (inProgress >= options.concurrency) return

inProgress++

const it = db.iterator({
keyAsBuffer: options.buffers,
valueAsBuffer: options.buffers
})

function loop () {
if (totalReads >= options.n) return end()
const start = process.hrtime()

it.next(function (err, key, value) {
if (err) throw err
if (key === undefined && value === undefined) return end()

const duration = process.hrtime(start)
const nano = (duration[0] * 1e9) + duration[1]

timesAccum += nano
totalBytes += Buffer.byteLength(key) + Buffer.byteLength(value)
totalReads++

if (totalReads % progressWindow === 0) {
console.log('' + inProgress, totalReads,
Math.round(totalReads / options.n * 100) + '%')
}

if (totalReads % window === 0) {
elapsed = Date.now() - startTime
stream.write(
elapsed +
',' + totalReads +
',' + totalBytes +
',' + (timesAccum / window).toFixed(3) +
',' + ((totalBytes / 1048576) / (elapsed / 1e3)).toFixed(3) +
'\n')
timesAccum = 0
}

loop()
})
}

function end () {
it.end(function (err) {
if (err) throw err
inProgress--
process.nextTick(iterate)
})
}

loop()
}

for (let i = 0; i < options.concurrency; i++) iterate()
}

factory(function (err, db) {
if (err) throw err

let entries = 0

function loop (err) {
if (err) throw err

console.log('Prep: wrote %d of %d entries', entries, options.n)
if (entries >= options.n) return setTimeout(() => start(db), 500)

const batch = db.batch()

for (let i = 0; i < 1e3 && entries < options.n; i++) {
const key = generator.key(entries++)
const value = generator.value()

batch.put(key, value)
}

batch.write(loop)
}

loop()
})
}
55 changes: 55 additions & 0 deletions benchmarks/iterate.plot.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
'use strict'

const e = require('../lib/escape-gnuplot-string')

module.exports = function (title, description, results) {
const durations = results.map(function (res, i) {
const file = res.csvFile
const title = res.id(results)

return `'${e(file)}' using ($1/1000):($4) title '${e(title)}' ls ${i + 1} axes x1y1`
})

const throughputs = results.map(function (res, i) {
const file = res.csvFile
const title = res.id(results)

return `'${e(file)}' using ($1/1000):($5) w lines title '${e(title)}' ls ${i + 1} axes x1y1`
})

return `
reset
set terminal pngcairo truecolor enhanced font "Ubuntu Mono,10" size 1920, 1080 background rgb "#1b1b1b"
set datafile separator ','
set autoscale y
set ytics mirror
set tics in
set xlabel "Time (seconds)" tc rgb "#999999"
set key outside tc rgb "#999999"
set border lc rgb "#999999"
# To plot more than 5 files, add more line styles
set style line 1 lt 7 ps 0.8 lc rgb "#00FFFF"
set style line 2 lt 7 ps 0.8 lc rgb "#D84797"
set style line 3 lt 7 ps 0.8 lc rgb "#23CE6B"
set style line 4 lt 7 ps 0.8 lc rgb "#F5B700"
set style line 5 lt 7 ps 0.8 lc rgb "#731DD8"
set multiplot layout 2,1
set lmargin at screen 0.1
set title '${e(title)}' tc rgb "#cccccc" offset 0,0.1 font "Ubuntu Mono,12"
set label 1 '${e(description)}' tc rgb "#999999" at graph 0.5,1.10 center front
set ylabel 'ns/read' tc rgb "#999999"
set logscale y
plot ${durations.join(', ')}
set title ""
set label 1 ""
set ylabel 'CMA Throughput MB/s' tc rgb "#999999"
set nologscale y
plot ${throughputs.join(', ')}
unset multiplot`
}

0 comments on commit d3797b0

Please sign in to comment.