-
Notifications
You must be signed in to change notification settings - Fork 0
BlobDictionary #732
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
BlobDictionary #732
Changes from 26 commits
Commits
Show all changes
37 commits
Select commit
Hold shift + click to select a range
8740aa0
wip
mateuszsikora 0fbf4a9
add BlobDictionary collection
mateuszsikora 132cb32
full compatiblity with HashDictionary
mateuszsikora a525c6d
add benchmarks
mateuszsikora be2a5ea
Merge branch 'main' into ms-blob-dict
mateuszsikora b552446
bug fixes
mateuszsikora 4c88f45
Merge branch 'main' into ms-blob-dict
mateuszsikora 5c477e0
fix part of build errors
mateuszsikora 46c78d6
fix part of build errors vol.2
mateuszsikora 1df0f0d
refactor
mateuszsikora 01407cd
add docs and tests
mateuszsikora ff67a3b
fix benchmarks
mateuszsikora 5de2758
fix circular deps
mateuszsikora 2bd5561
simplify TruncatedHashDictionary impementation
mateuszsikora a1e3efb
cleaning
mateuszsikora 7687f47
fix tests
mateuszsikora 008d115
add better docs
mateuszsikora 8a98b93
uncomment tests
mateuszsikora 78fc49a
better implementation of bytesAsU48
mateuszsikora a49321c
fix tests
mateuszsikora 2393348
more benchmarks
mateuszsikora 849db62
review fixes
mateuszsikora 7afd6a9
more benchmarks and review fixes
mateuszsikora ce51d6d
Merge branch 'main' into ms-blob-dict
mateuszsikora a9d64cd
simpler benchmarks and more review fixes
mateuszsikora c575c11
Merge branch 'ms-blob-dict' of github.com:FluffyLabs/typeberry into m…
mateuszsikora 8c9af5a
fix typo
mateuszsikora 842b27f
fix CI workflow
mateuszsikora cb1d6c0
fix CI workflow vol.2
mateuszsikora b9ae614
Merge branch 'main' into ms-blob-dict
mateuszsikora cfe2799
Merge branch 'main' into ms-blob-dict
mateuszsikora 493f501
review fixes
mateuszsikora 42333c8
Merge branch 'main' into ms-blob-dict
mateuszsikora 430b073
Merge branch 'main' into ms-blob-dict
mateuszsikora 1401f96
review fixes
mateuszsikora ce47e57
Merge branch 'main' into ms-blob-dict
mateuszsikora 41e6590
Merge branch 'main' into ms-blob-dict
mateuszsikora File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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
123
benchmarks/collections/hash-dict-vs-blob-dict_delete.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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"; | ||
tomusdrw marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| 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}))`, | ||
|
|
||
tomusdrw marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| 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(); | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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"; | ||
mateuszsikora marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| 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}))`, | ||
coderabbitai[bot] marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| 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(); | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.