Skip to content
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
8740aa0
wip
mateuszsikora Oct 6, 2025
0fbf4a9
add BlobDictionary collection
mateuszsikora Oct 19, 2025
132cb32
full compatiblity with HashDictionary
mateuszsikora Oct 20, 2025
a525c6d
add benchmarks
mateuszsikora Oct 20, 2025
be2a5ea
Merge branch 'main' into ms-blob-dict
mateuszsikora Oct 20, 2025
b552446
bug fixes
mateuszsikora Oct 22, 2025
4c88f45
Merge branch 'main' into ms-blob-dict
mateuszsikora Oct 22, 2025
5c477e0
fix part of build errors
mateuszsikora Oct 22, 2025
46c78d6
fix part of build errors vol.2
mateuszsikora Oct 22, 2025
1df0f0d
refactor
mateuszsikora Oct 22, 2025
01407cd
add docs and tests
mateuszsikora Oct 23, 2025
ff67a3b
fix benchmarks
mateuszsikora Oct 23, 2025
5de2758
fix circular deps
mateuszsikora Oct 23, 2025
2bd5561
simplify TruncatedHashDictionary impementation
mateuszsikora Oct 24, 2025
a1e3efb
cleaning
mateuszsikora Oct 24, 2025
7687f47
fix tests
mateuszsikora Oct 24, 2025
008d115
add better docs
mateuszsikora Oct 24, 2025
8a98b93
uncomment tests
mateuszsikora Oct 24, 2025
78fc49a
better implementation of bytesAsU48
mateuszsikora Oct 24, 2025
a49321c
fix tests
mateuszsikora Oct 24, 2025
2393348
more benchmarks
mateuszsikora Oct 24, 2025
849db62
review fixes
mateuszsikora Oct 24, 2025
7afd6a9
more benchmarks and review fixes
mateuszsikora Oct 27, 2025
ce51d6d
Merge branch 'main' into ms-blob-dict
mateuszsikora Oct 27, 2025
a9d64cd
simpler benchmarks and more review fixes
mateuszsikora Oct 28, 2025
c575c11
Merge branch 'ms-blob-dict' of github.com:FluffyLabs/typeberry into m…
mateuszsikora Oct 28, 2025
8c9af5a
fix typo
mateuszsikora Oct 28, 2025
842b27f
fix CI workflow
mateuszsikora Oct 28, 2025
cb1d6c0
fix CI workflow vol.2
mateuszsikora Oct 28, 2025
b9ae614
Merge branch 'main' into ms-blob-dict
mateuszsikora Nov 14, 2025
cfe2799
Merge branch 'main' into ms-blob-dict
mateuszsikora Nov 17, 2025
493f501
review fixes
mateuszsikora Nov 17, 2025
42333c8
Merge branch 'main' into ms-blob-dict
mateuszsikora Nov 17, 2025
430b073
Merge branch 'main' into ms-blob-dict
mateuszsikora Nov 18, 2025
1401f96
review fixes
mateuszsikora Nov 18, 2025
ce47e57
Merge branch 'main' into ms-blob-dict
mateuszsikora Nov 18, 2025
41e6590
Merge branch 'main' into ms-blob-dict
mateuszsikora Nov 18, 2025
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
79 changes: 79 additions & 0 deletions benchmarks/bytes/bytes-to-number.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { pathToFileURL } from "node:url";
import { add, complete, configure, cycle, save, suite } from "@typeberry/benchmark/setup.js";
import { Bytes } from "@typeberry/bytes";

const HASH_SIZE = 6;
const ARRAY_SIZE = 10_000;

function bytesAsU48WithoutBitOps(bytes: Uint8Array): number {
const len = bytes.length;

let value = 0;
for (let i = 0; i < len; i++) {
value = value * 256 + bytes[i];
}

return value * 8 + len;
}

function bytesAsU48WithBitOps(bytes: Uint8Array): number {
const len = bytes.length;

let value = bytes[3] | (bytes[2] << 8) | (bytes[1] << 16) | (bytes[0] << 24);

for (let i = 4; i < bytes.length; i++) {
value = value * 256 + bytes[i];
}

return value * 8 + len;
}

function generateHash<T extends number>(len: T): Bytes<T> {
const result: number[] = [];
for (let i = 0; i < len; i += 1) {
const val = Math.floor(Math.random() * 255);
result.push(val);
}
return Bytes.fromNumbers(result, len);
}

function generateArrayOfHashes<T extends number>(size: number, hashLen: T): Bytes<T>[] {
const res: Bytes<T>[] = [];
for (let i = 0; i < size; i += 1) {
res.push(generateHash(hashLen));
}
return res;
}

const arr = generateArrayOfHashes(ARRAY_SIZE, HASH_SIZE);

/**
* Comparing conversion 6 bytes into U48 number using two functions:
* - ugly one that converts 4 bytes into U32 firstly using bit operations and then 2 bytes using math operations
* - a bit prettier one that convers all bytes using math operations
*/
export default function run() {
return suite(
"Bytes into number comparison",
add("Conversion with bitops ", () => {
return () => {
arr.map((x) => bytesAsU48WithBitOps(x.raw));
};
}),

add("Conversion without bitops ", () => {
return () => {
arr.map((x) => bytesAsU48WithoutBitOps(x.raw));
};
}),

cycle(),
complete(),
configure({}),
...save(import.meta.filename),
);
}

if (import.meta.url === pathToFileURL(process.argv[1]).href) {
run();
}
123 changes: 123 additions & 0 deletions benchmarks/collections/hash-dict-vs-blob-dict_delete.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import { pathToFileURL } from "node:url";
import { add, complete, configure, cycle, save, suite } from "@typeberry/benchmark/setup.js";
import { Bytes } from "@typeberry/bytes";
import { StringHashDictionary } from "@typeberry/collections";
import { BlobDictionary } from "@typeberry/collections/blob-dictionary.js";
import { HASH_SIZE, type OpaqueHash } from "@typeberry/hash";
import blake2b from "blake2b";

const NO_OF_KEYS = 1000;

function blake2bHasher(bytes: Bytes<HASH_SIZE>) {
const hasher = blake2b(HASH_SIZE);
return hasher.update(bytes.raw).digest("binary");
}
function longCollisionKey(n: number) {
const key = Bytes.blobFromString(`${n}`);
const ret = Bytes.zero(HASH_SIZE);
ret.raw.set(key.raw, 0);
ret.raw.reverse();
return ret;
}

function hashKey(n: number) {
return Bytes.fromBlob(blake2bHasher(longCollisionKey(n)), HASH_SIZE);
}

const LONG_COLLISION_KEYS = Array.from({ length: NO_OF_KEYS }, (_, i) => longCollisionKey(i));
const HASH_KEYS = Array.from({ length: NO_OF_KEYS }, (_, i) => hashKey(i));
const MIN_THRESHOLD = 1;
const MAX_THRESHOLD = 6;

export default function run() {
const promises: ReturnType<typeof suite>[] = [];

const longCollisionKeysBlobDictBenchmarks: ReturnType<typeof add>[] = [];

for (let threshold = MIN_THRESHOLD; threshold < MAX_THRESHOLD; threshold++) {
longCollisionKeysBlobDictBenchmarks.push(
add(`BlobDictionary(${threshold})`, () => {
const map = BlobDictionary.new<OpaqueHash, number>(threshold);
for (let k = 0; k < NO_OF_KEYS; k += 1) {
map.set(LONG_COLLISION_KEYS[k], k);
}
return () => {
for (let k = 0; k < NO_OF_KEYS; k += 1) {
map.delete(LONG_COLLISION_KEYS[k]);
}
};
}),
);
}
const longCollisionTestPromise = suite(
`Comparing delete operation in two hash dicts using long collition keys and BlobDictionary(n: [${MIN_THRESHOLD}: ${MAX_THRESHOLD}))`,

add("StringHashDictionary", () => {
const map = StringHashDictionary.new<OpaqueHash, number>();
for (let k = 0; k < NO_OF_KEYS; k += 1) {
map.set(LONG_COLLISION_KEYS[k], k);
}
return () => {
for (let k = 0; k < NO_OF_KEYS; k += 1) {
map.delete(LONG_COLLISION_KEYS[k]);
}
};
}),

...longCollisionKeysBlobDictBenchmarks,

cycle(),
complete(),
configure({}),
...save(import.meta.filename.replace(".ts", "-long-collision-key.ts")),
);

const hashKeysBlobDictBenchmarks: ReturnType<typeof add>[] = [];

for (let threshold = MIN_THRESHOLD; threshold < MAX_THRESHOLD; threshold++) {
hashKeysBlobDictBenchmarks.push(
add(`BlobDictionary(${threshold})`, () => {
const map = BlobDictionary.new<OpaqueHash, number>(threshold);
for (let k = 0; k < NO_OF_KEYS; k += 1) {
map.set(HASH_KEYS[k], k);
}
return () => {
for (let k = 0; k < NO_OF_KEYS; k += 1) {
map.delete(HASH_KEYS[k]);
}
};
}),
);
}

const hashKeyTestPromise = suite(
`Comparing set operation in two hash dicts using hash keys and BlobDictionary(n: [${MIN_THRESHOLD}: ${MAX_THRESHOLD}))`,

add("StringHashDictionary", () => {
const map = StringHashDictionary.new<OpaqueHash, number>();
for (let k = 0; k < NO_OF_KEYS; k += 1) {
map.set(HASH_KEYS[k], k);
}
return () => {
for (let k = 0; k < NO_OF_KEYS; k += 1) {
map.delete(HASH_KEYS[k]);
}
};
}),

...hashKeysBlobDictBenchmarks,

cycle(),
complete(),
configure({}),
...save(import.meta.filename.replace(".ts", "-hash-key.ts")),
);

promises.push(longCollisionTestPromise, hashKeyTestPromise);

return Promise.allSettled(promises);
}

if (import.meta.url === pathToFileURL(process.argv[1]).href) {
run();
}
124 changes: 124 additions & 0 deletions benchmarks/collections/hash-dict-vs-blob-dict_get.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import { pathToFileURL } from "node:url";
import { add, complete, configure, cycle, save, suite } from "@typeberry/benchmark/setup.js";
import { Bytes } from "@typeberry/bytes";
import { StringHashDictionary } from "@typeberry/collections";
import { BlobDictionary } from "@typeberry/collections/blob-dictionary.js";
import { HASH_SIZE, type OpaqueHash } from "@typeberry/hash";
import blake2b from "blake2b";

const NO_OF_KEYS = 1000;

function blake2bHasher(bytes: Bytes<HASH_SIZE>) {
const hasher = blake2b(HASH_SIZE);
return hasher.update(bytes.raw).digest("binary");
}
function longCollisionKey(n: number) {
const key = Bytes.blobFromString(`${n}`);
const ret = Bytes.zero(HASH_SIZE);
ret.raw.set(key.raw, 0);
ret.raw.reverse();
return ret;
}

function hashKey(n: number) {
return Bytes.fromBlob(blake2bHasher(longCollisionKey(n)), HASH_SIZE);
}

const LONG_COLLISION_KEYS = Array.from({ length: NO_OF_KEYS }, (_, i) => longCollisionKey(i));
const HASH_KEYS = Array.from({ length: NO_OF_KEYS }, (_, i) => hashKey(i));
const MIN_THRESHOLD = 1;
const MAX_THRESHOLD = 6;

export default function run() {
const promises: ReturnType<typeof suite>[] = [];

const longCollisionKeysBlobDictBenchmarks: ReturnType<typeof add>[] = [];

for (let threshold = MIN_THRESHOLD; threshold < MAX_THRESHOLD; threshold++) {
longCollisionKeysBlobDictBenchmarks.push(
add(`BlobDictionary(${threshold})`, () => {
const map = BlobDictionary.new<OpaqueHash, number>(threshold);
for (let k = 0; k < NO_OF_KEYS; k += 1) {
map.set(LONG_COLLISION_KEYS[k], k);
}
return () => {
for (let k = 0; k < NO_OF_KEYS; k += 1) {
map.get(LONG_COLLISION_KEYS[k]);
}
};
}),
);
}
const longCollisionTestPromise = suite(
`Comparing get operation in two hash dicts using long collition keys and BlobDictionary(n: [${MIN_THRESHOLD}: ${MAX_THRESHOLD}))`,

add("StringHashDictionary", () => {
const map = StringHashDictionary.new<OpaqueHash, number>();
for (let k = 0; k < NO_OF_KEYS; k += 1) {
map.set(LONG_COLLISION_KEYS[k], k);
}

return () => {
for (let k = 0; k < NO_OF_KEYS; k += 1) {
map.get(LONG_COLLISION_KEYS[k]);
}
};
}),

...longCollisionKeysBlobDictBenchmarks,

cycle(),
complete(),
configure({}),
...save(import.meta.filename.replace(".ts", "-long-collision-key.ts")),
);

const hashKeysBlobDictBenchmarks: ReturnType<typeof add>[] = [];

for (let threshold = MIN_THRESHOLD; threshold < MAX_THRESHOLD; threshold++) {
hashKeysBlobDictBenchmarks.push(
add(`BlobDictionary(${threshold})`, () => {
const map = BlobDictionary.new<OpaqueHash, number>(threshold);
for (let k = 0; k < NO_OF_KEYS; k += 1) {
map.set(HASH_KEYS[k], k);
}
return () => {
for (let k = 0; k < NO_OF_KEYS; k += 1) {
map.get(HASH_KEYS[k]);
}
};
}),
);
}

const hashKeyTestPromise = suite(
`Comparing set operation in two hash dicts using hash keys and BlobDictionary(n: [${MIN_THRESHOLD}: ${MAX_THRESHOLD}))`,

add("StringHashDictionary", () => {
const map = StringHashDictionary.new<OpaqueHash, number>();
for (let k = 0; k < NO_OF_KEYS; k += 1) {
map.set(HASH_KEYS[k], k);
}
return () => {
for (let k = 0; k < NO_OF_KEYS; k += 1) {
map.get(HASH_KEYS[k]);
}
};
}),

...hashKeysBlobDictBenchmarks,

cycle(),
complete(),
configure({}),
...save(import.meta.filename.replace(".ts", "-hash-key.ts")),
);

promises.push(longCollisionTestPromise, hashKeyTestPromise);

return Promise.allSettled(promises);
}

if (import.meta.url === pathToFileURL(process.argv[1]).href) {
run();
}
Loading
Loading