9baf9b23d8
* fanout to 4 * move meta to root * combine repoRoot & commit * finish up commits & tests * cleanup * tidy * better diff * fixed up mst tests for bytes & fanout 4 * added tests for allowable chars * length limit & check on deserialize * tidy * better leading zeros fn * pr feedback * working on migration * Setup app migration machinery * Skip appMigration() tests on sqlite * migration working * make an app_migration table * patch up test * sanity check * wrapped up * working * remove run on repov2 * delete seqs * logs & static migration names * cleanup * missed a file --------- Co-authored-by: Devin Ivy <devinivy@gmail.com>
166 lines
4.7 KiB
TypeScript
166 lines
4.7 KiB
TypeScript
import { CID } from 'multiformats'
|
|
import { Fanout, MemoryBlockstore, MST, NodeEntry } from '../src'
|
|
import * as util from '../tests/_util'
|
|
import fs from 'fs'
|
|
|
|
type BenchmarkData = {
|
|
fanout: number
|
|
size: number
|
|
addTime: string
|
|
saveTime: string
|
|
walkTime: string
|
|
depth: number
|
|
maxWidth: number
|
|
blockstoreSize: number
|
|
largestProofSize: number
|
|
avgProofSize: number
|
|
widths: Record<number, number>
|
|
}
|
|
|
|
describe('MST Benchmarks', () => {
|
|
let mapping: Record<string, CID>
|
|
let shuffled: [string, CID][]
|
|
|
|
const size = 500000
|
|
|
|
beforeAll(async () => {
|
|
mapping = await util.generateBulkDataKeys(size)
|
|
shuffled = util.shuffle(Object.entries(mapping))
|
|
})
|
|
|
|
// const fanouts: Fanout[] = [8, 16, 32]
|
|
const fanouts: Fanout[] = [16, 32]
|
|
it('benchmarks various fanouts', async () => {
|
|
const benches: BenchmarkData[] = []
|
|
for (const fanout of fanouts) {
|
|
const blockstore = new MemoryBlockstore()
|
|
let mst = await MST.create(blockstore, [], { fanout })
|
|
|
|
const start = Date.now()
|
|
|
|
for (const entry of shuffled) {
|
|
mst = await mst.add(entry[0], entry[1])
|
|
}
|
|
|
|
const doneAdding = Date.now()
|
|
|
|
const root = await util.saveMst(blockstore, mst)
|
|
|
|
const doneSaving = Date.now()
|
|
|
|
const reloaded = await MST.load(blockstore, root, { fanout })
|
|
const widthTracker = new NodeWidths()
|
|
for await (const entry of reloaded.walk()) {
|
|
await widthTracker.trackEntry(entry)
|
|
}
|
|
|
|
const doneWalking = Date.now()
|
|
|
|
const paths = await reloaded.paths()
|
|
let largestProof = 0
|
|
let combinedProofSizes = 0
|
|
for (const path of paths) {
|
|
let proofSize = 0
|
|
for (const entry of path) {
|
|
if (entry.isTree()) {
|
|
const bytes = await blockstore.getBytes(entry.pointer)
|
|
if (!bytes) {
|
|
throw new Error(`Bytes not found: ${entry.pointer}`)
|
|
}
|
|
proofSize += bytes.byteLength
|
|
}
|
|
}
|
|
largestProof = Math.max(largestProof, proofSize)
|
|
combinedProofSizes += proofSize
|
|
}
|
|
const avgProofSize = Math.ceil(combinedProofSizes / paths.length)
|
|
|
|
const blockstoreSize = await blockstore.sizeInBytes()
|
|
|
|
benches.push({
|
|
fanout,
|
|
size,
|
|
addTime: secDiff(start, doneAdding),
|
|
saveTime: secDiff(doneAdding, doneSaving),
|
|
walkTime: secDiff(doneSaving, doneWalking),
|
|
depth: await mst.getLayer(),
|
|
blockstoreSize,
|
|
largestProofSize: largestProof,
|
|
avgProofSize: avgProofSize,
|
|
maxWidth: widthTracker.max,
|
|
widths: widthTracker.data,
|
|
})
|
|
}
|
|
writeBenchData(benches, 'mst-benchmarks')
|
|
})
|
|
})
|
|
|
|
const secDiff = (first: number, second: number): string => {
|
|
return ((second - first) / 1000).toFixed(3)
|
|
}
|
|
|
|
class NodeWidths {
|
|
data = {
|
|
0: 0,
|
|
16: 0,
|
|
32: 0,
|
|
48: 0,
|
|
64: 0,
|
|
96: 0,
|
|
128: 0,
|
|
160: 0,
|
|
192: 0,
|
|
224: 0,
|
|
256: 0,
|
|
}
|
|
max = 0
|
|
|
|
async trackEntry(entry: NodeEntry) {
|
|
if (!entry.isTree()) return
|
|
const entries = await entry.getEntries()
|
|
const width = entries.filter((e) => e.isLeaf()).length
|
|
this.max = Math.max(this.max, width)
|
|
if (width >= 0) this.data[0]++
|
|
if (width >= 16) this.data[16]++
|
|
if (width >= 32) this.data[32]++
|
|
if (width >= 48) this.data[48]++
|
|
if (width >= 64) this.data[64]++
|
|
if (width >= 96) this.data[96]++
|
|
if (width >= 128) this.data[128]++
|
|
if (width >= 160) this.data[160]++
|
|
if (width >= 192) this.data[192]++
|
|
if (width >= 224) this.data[224]++
|
|
if (width >= 256) this.data[256]++
|
|
}
|
|
}
|
|
|
|
const writeBenchData = (benches: BenchmarkData[], fileLoc: string) => {
|
|
let toWrite = ''
|
|
for (const bench of benches) {
|
|
toWrite += `Fanout: ${bench.fanout}
|
|
----------------------
|
|
Time to add ${bench.size} leaves: ${bench.addTime}s
|
|
Time to save tree with ${bench.size} leaves: ${bench.saveTime}s
|
|
Time to reconstruct & walk ${bench.size} leaves: ${bench.walkTime}s
|
|
Tree depth: ${bench.depth}
|
|
Max Node Width (only counting leaves): ${bench.maxWidth}
|
|
The total blockstore size is: ${bench.blockstoreSize} bytes
|
|
Largest proof size: ${bench.largestProofSize} bytes
|
|
Average proof size: ${bench.avgProofSize} bytes
|
|
Nodes with >= 0 leaves: ${bench.widths[0]}
|
|
Nodes with >= 16 leaves: ${bench.widths[16]}
|
|
Nodes with >= 32 leaves: ${bench.widths[32]}
|
|
Nodes with >= 48 leaves: ${bench.widths[48]}
|
|
Nodes with >= 64 leaves: ${bench.widths[64]}
|
|
Nodes with >= 96 leaves: ${bench.widths[96]}
|
|
Nodes with >= 128 leaves: ${bench.widths[128]}
|
|
Nodes with >= 160 leaves: ${bench.widths[160]}
|
|
Nodes with >= 192 leaves: ${bench.widths[192]}
|
|
Nodes with >= 224 leaves: ${bench.widths[224]}
|
|
Nodes with >= 256 leaves: ${bench.widths[256]}
|
|
|
|
`
|
|
}
|
|
fs.writeFileSync(fileLoc, toWrite)
|
|
}
|