Skip to content

Commit cbb5b6a

Browse files
authored
Merge pull request #382 from bsv-blockchain/housekeeping
Housekeeping
2 parents 80965ad + 8a730fd commit cbb5b6a

File tree

15 files changed

+2467
-1575
lines changed

15 files changed

+2467
-1575
lines changed

benchmarks/atomic-beef-bench.js

Lines changed: 9 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import { performance } from 'perf_hooks'
21
import Transaction from '../dist/esm/src/transaction/Transaction.js'
32
import PrivateKey from '../dist/esm/src/primitives/PrivateKey.js'
43
import P2PKH from '../dist/esm/src/script/templates/P2PKH.js'
4+
import { runBenchmark } from './lib/benchmark-runner.js'
55

66
async function buildChain (depth) {
77
const privateKey = new PrivateKey(1)
@@ -34,26 +34,6 @@ async function buildChain (depth) {
3434

3535
return currentTx
3636
}
37-
38-
async function measure (fn, iterations = 5) {
39-
const times = []
40-
for (let i = 0; i < iterations; i++) {
41-
const start = performance.now()
42-
const result = fn()
43-
if (result instanceof Promise) {
44-
await result
45-
}
46-
const end = performance.now()
47-
times.push(end - start)
48-
}
49-
const total = times.reduce((sum, t) => sum + t, 0)
50-
return {
51-
average: total / times.length,
52-
min: Math.min(...times),
53-
max: Math.max(...times)
54-
}
55-
}
56-
5737
async function run () {
5838
const depth = Number.parseInt(process.argv[2] ?? '200', 10)
5939
const iterations = Number.parseInt(process.argv[3] ?? '5', 10)
@@ -64,12 +44,15 @@ async function run () {
6444
const initialSerialized = finalTx.toAtomicBEEF()
6545
Transaction.fromAtomicBEEF(initialSerialized)
6646

67-
const toStats = await measure(() => finalTx.toAtomicBEEF(), iterations)
6847
const serialized = finalTx.toAtomicBEEF()
69-
const fromStats = await measure(() => Transaction.fromAtomicBEEF(serialized), iterations)
70-
71-
console.log(`Transaction.toAtomicBEEF avg: ${toStats.average.toFixed(2)}ms (min ${toStats.min.toFixed(2)}ms, max ${toStats.max.toFixed(2)}ms)`)
72-
console.log(`Transaction.fromAtomicBEEF avg: ${fromStats.average.toFixed(2)}ms (min ${fromStats.min.toFixed(2)}ms, max ${fromStats.max.toFixed(2)}ms)`)
48+
await runBenchmark('Transaction.toAtomicBEEF', () => finalTx.toAtomicBEEF(), {
49+
minSampleMs: 200,
50+
samples: Math.max(5, iterations)
51+
})
52+
await runBenchmark('Transaction.fromAtomicBEEF', () => Transaction.fromAtomicBEEF(serialized), {
53+
minSampleMs: 200,
54+
samples: Math.max(5, iterations)
55+
})
7356
}
7457

7558
run().catch((err) => {

benchmarks/bignumber-bench.js

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,5 @@
1-
import { performance } from 'perf_hooks'
21
import BigNumber from '../dist/esm/src/primitives/BigNumber.js'
3-
4-
function benchmark (name, fn) {
5-
const start = performance.now()
6-
fn()
7-
const end = performance.now()
8-
console.log(`${name}: ${(end - start).toFixed(2)}ms`)
9-
}
2+
import { runBenchmark } from './lib/benchmark-runner.js'
103

114
const digits = Number(process.argv[2] ?? 20000)
125
const mulIterations = Number(process.argv[3] ?? 5)
@@ -15,14 +8,27 @@ const largeHex = 'f'.repeat(digits)
158
const a = new BigNumber(largeHex, 16)
169
const b = new BigNumber(largeHex, 16)
1710

18-
benchmark('mul large numbers', () => {
19-
for (let i = 0; i < mulIterations; i++) {
20-
a.mul(b)
21-
}
22-
})
11+
async function main () {
12+
await runBenchmark('mul large numbers', () => {
13+
for (let i = 0; i < mulIterations; i++) {
14+
a.mul(b)
15+
}
16+
}, {
17+
minSampleMs: 500,
18+
samples: 10
19+
})
20+
21+
await runBenchmark('add large numbers', () => {
22+
for (let i = 0; i < addIterations; i++) {
23+
a.add(b)
24+
}
25+
}, {
26+
minSampleMs: 400,
27+
samples: 10
28+
})
29+
}
2330

24-
benchmark('add large numbers', () => {
25-
for (let i = 0; i < addIterations; i++) {
26-
a.add(b)
27-
}
31+
main().catch((err) => {
32+
console.error(err)
33+
process.exit(1)
2834
})
Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,5 @@
1-
import { performance } from 'perf_hooks'
21
import BigNumber from '../dist/esm/src/primitives/BigNumber.js'
3-
4-
function benchmark (name, fn) {
5-
const start = performance.now()
6-
fn()
7-
const end = performance.now()
8-
console.log(`${name}: ${(end - start).toFixed(2)}ms`)
9-
}
2+
import { runBenchmark } from './lib/benchmark-runner.js'
103

114
const digits = Number(process.argv[2] ?? 200000)
125
const iterations = Number(process.argv[3] ?? 1)
@@ -15,22 +8,31 @@ const bn = new BigNumber(largeHex, 16)
158
const little = bn.toSm('little')
169
const big = bn.toSm('big')
1710

18-
benchmark('toSm big', () => {
19-
for (let i = 0; i < iterations; i++) bn.toSm('big')
20-
})
11+
async function main () {
12+
const options = { minSampleMs: 400, samples: 8 }
2113

22-
benchmark('toSm little', () => {
23-
for (let i = 0; i < iterations; i++) bn.toSm('little')
24-
})
14+
await runBenchmark('toSm big', () => {
15+
for (let i = 0; i < iterations; i++) bn.toSm('big')
16+
}, options)
2517

26-
benchmark('fromSm big', () => {
27-
for (let i = 0; i < iterations; i++) BigNumber.fromSm(big)
28-
})
18+
await runBenchmark('toSm little', () => {
19+
for (let i = 0; i < iterations; i++) bn.toSm('little')
20+
}, options)
2921

30-
benchmark('fromSm little', () => {
31-
for (let i = 0; i < iterations; i++) BigNumber.fromSm(little, 'little')
32-
})
22+
await runBenchmark('fromSm big', () => {
23+
for (let i = 0; i < iterations; i++) BigNumber.fromSm(big)
24+
}, options)
25+
26+
await runBenchmark('fromSm little', () => {
27+
for (let i = 0; i < iterations; i++) BigNumber.fromSm(little, 'little')
28+
}, options)
29+
30+
await runBenchmark('fromScriptNum', () => {
31+
for (let i = 0; i < iterations; i++) BigNumber.fromScriptNum(little)
32+
}, options)
33+
}
3334

34-
benchmark('fromScriptNum', () => {
35-
for (let i = 0; i < iterations; i++) BigNumber.fromScriptNum(little)
35+
main().catch((err) => {
36+
console.error(err)
37+
process.exit(1)
3638
})

benchmarks/lib/benchmark-runner.js

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import { performance } from 'perf_hooks'
2+
3+
function numberFromEnv (key, fallback) {
4+
const value = Number(process.env[key])
5+
return Number.isFinite(value) && value > 0 ? value : fallback
6+
}
7+
8+
const DEFAULTS = {
9+
warmupIterations: numberFromEnv('BENCH_WARMUP', 2),
10+
samples: numberFromEnv('BENCH_SAMPLES', 8),
11+
minSampleMs: numberFromEnv('BENCH_MIN_SAMPLE_MS', 250),
12+
minIterations: numberFromEnv('BENCH_MIN_ITERATIONS', 1)
13+
}
14+
15+
async function maybeAwait (result) {
16+
if (result != null && typeof result.then === 'function') {
17+
await result
18+
}
19+
}
20+
21+
async function measureSample (fn, minSampleMs, minIterations) {
22+
let iterations = 0
23+
let elapsed = 0
24+
do {
25+
const start = performance.now()
26+
await maybeAwait(fn())
27+
elapsed += performance.now() - start
28+
iterations++
29+
} while (elapsed < minSampleMs || iterations < minIterations)
30+
31+
return {
32+
average: elapsed / iterations,
33+
iterations,
34+
elapsed
35+
}
36+
}
37+
38+
function computeStats (values) {
39+
const total = values.reduce((sum, value) => sum + value, 0)
40+
const average = total / values.length
41+
const min = Math.min(...values)
42+
const max = Math.max(...values)
43+
const variance = values.length > 1
44+
? values.reduce((sum, value) => sum + Math.pow(value - average, 2), 0) / values.length
45+
: 0
46+
return {
47+
average,
48+
min,
49+
max,
50+
stddev: Math.sqrt(variance),
51+
samples: values.length
52+
}
53+
}
54+
55+
function logStats (name, stats, iterations) {
56+
const avgIterations = iterations.reduce((sum, value) => sum + value, 0) / iterations.length
57+
console.log(
58+
`${name}: ${stats.average.toFixed(2)}ms avg (min ${stats.min.toFixed(2)}ms, max ${stats.max.toFixed(2)}ms, ` +
59+
${stats.stddev.toFixed(2)}ms, ~${avgIterations.toFixed(1)} runs/sample)`
60+
)
61+
}
62+
63+
export async function runBenchmark (name, fn, options = {}) {
64+
const warmupIterations = Math.max(0, Math.floor(options.warmupIterations ?? DEFAULTS.warmupIterations))
65+
const samples = Math.max(1, Math.floor(options.samples ?? DEFAULTS.samples))
66+
const minSampleMs = Math.max(1, options.minSampleMs ?? DEFAULTS.minSampleMs)
67+
const minIterations = Math.max(1, Math.floor(options.minIterations ?? DEFAULTS.minIterations))
68+
69+
for (let i = 0; i < warmupIterations; i++) {
70+
await maybeAwait(fn())
71+
}
72+
73+
const sampleValues = []
74+
const sampleIterations = []
75+
for (let i = 0; i < samples; i++) {
76+
const { average, iterations } = await measureSample(fn, minSampleMs, minIterations)
77+
sampleValues.push(average)
78+
sampleIterations.push(iterations)
79+
}
80+
81+
const stats = computeStats(sampleValues)
82+
logStats(name, stats, sampleIterations)
83+
return {
84+
...stats,
85+
sampleIterations
86+
}
87+
}

benchmarks/reader-writer-bench.js

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,12 @@
1-
import { performance } from 'perf_hooks'
21
import { Writer, Reader } from '../dist/esm/src/primitives/utils.js'
2+
import { runBenchmark } from './lib/benchmark-runner.js'
33

44
function data (len, start = 0) {
55
const arr = new Array(len)
66
for (let i = 0; i < len; i++) arr[i] = (start + i) & 0xff
77
return arr
88
}
99

10-
function benchmark (name, fn) {
11-
const start = performance.now()
12-
fn()
13-
const end = performance.now()
14-
console.log(`${name}: ${(end - start).toFixed(2)}ms`)
15-
}
16-
1710
const largePayloads = [data(2 * 1024 * 1024, 0), data(2 * 1024 * 1024, 1), data(2 * 1024 * 1024, 2)]
1811
const smallPayloads = Array.from({ length: 3000 }, (_, i) => data(100, i))
1912
const mediumPayloads = Array.from({ length: 400 }, (_, i) => data(10 * 1024, i))
@@ -82,7 +75,15 @@ function rwMedium () {
8275
}
8376
}
8477

85-
benchmark('mixed ops', mixedOps)
86-
benchmark('large payloads', rwLarge)
87-
benchmark('3000 small payloads', rwSmall)
88-
benchmark('400 medium payloads', rwMedium)
78+
async function main () {
79+
const options = { minSampleMs: 300, samples: 9 }
80+
await runBenchmark('mixed ops', mixedOps, options)
81+
await runBenchmark('large payloads', rwLarge, options)
82+
await runBenchmark('3000 small payloads', rwSmall, options)
83+
await runBenchmark('400 medium payloads', rwMedium, options)
84+
}
85+
86+
main().catch((err) => {
87+
console.error(err)
88+
process.exit(1)
89+
})

benchmarks/script-serialization-bench.js

Lines changed: 16 additions & 13 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

benchmarks/symmetric-key-bench.js

Lines changed: 33 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,51 @@
1-
import { performance } from 'perf_hooks'
21
import { randomBytes, randomFillSync } from 'crypto'
32
// Provide browser-like crypto for Random
43
globalThis.self = { crypto: { getRandomValues: (arr) => randomFillSync(arr) } }
54
import SymmetricKey from '../dist/esm/src/primitives/SymmetricKey.js'
5+
import { runBenchmark } from './lib/benchmark-runner.js'
66

77
function rand (n) {
88
return [...randomBytes(n)]
99
}
1010

11-
function benchmark (name, fn) {
12-
const start = performance.now()
13-
fn()
14-
const end = performance.now()
15-
console.log(`${name}: ${(end - start).toFixed(2)}ms`)
16-
}
17-
1811
const key = SymmetricKey.fromRandom()
1912
const largeMsg = rand(2 * 1024 * 1024)
2013
const smallMsgs = Array.from({ length: 50 }, () => rand(100))
2114
const mediumMsgs = Array.from({ length: 200 }, () => rand(1024))
2215

2316
let enc
24-
benchmark('encrypt large 2MB', () => {
25-
enc = key.encrypt(largeMsg)
26-
})
27-
benchmark('decrypt large 2MB', () => {
28-
key.decrypt(enc)
29-
})
30-
31-
benchmark('encrypt 50 small', () => {
32-
for (const m of smallMsgs) key.encrypt(m)
33-
})
3417
const encSmall = smallMsgs.map(m => key.encrypt(m))
35-
benchmark('decrypt 50 small', () => {
36-
for (const m of encSmall) key.decrypt(m)
37-
})
38-
39-
benchmark('encrypt 200 medium', () => {
40-
for (const m of mediumMsgs) key.encrypt(m)
41-
})
4218
const encMedium = mediumMsgs.map(m => key.encrypt(m))
43-
benchmark('decrypt 200 medium', () => {
44-
for (const m of encMedium) key.decrypt(m)
19+
20+
async function main () {
21+
const heavyOptions = { minSampleMs: 250, samples: 5, warmupIterations: 1 }
22+
const lightOptions = { minSampleMs: 250, samples: 9, warmupIterations: 1 }
23+
await runBenchmark('encrypt large 2MB', () => {
24+
enc = key.encrypt(largeMsg)
25+
}, heavyOptions)
26+
27+
await runBenchmark('decrypt large 2MB', () => {
28+
key.decrypt(enc)
29+
}, heavyOptions)
30+
31+
await runBenchmark('encrypt 50 small', () => {
32+
for (const m of smallMsgs) key.encrypt(m)
33+
}, lightOptions)
34+
35+
await runBenchmark('decrypt 50 small', () => {
36+
for (const m of encSmall) key.decrypt(m)
37+
}, lightOptions)
38+
39+
await runBenchmark('encrypt 200 medium', () => {
40+
for (const m of mediumMsgs) key.encrypt(m)
41+
}, lightOptions)
42+
43+
await runBenchmark('decrypt 200 medium', () => {
44+
for (const m of encMedium) key.decrypt(m)
45+
}, lightOptions)
46+
}
47+
48+
main().catch((err) => {
49+
console.error(err)
50+
process.exit(1)
4551
})

0 commit comments

Comments
 (0)