-
Notifications
You must be signed in to change notification settings - Fork 678
perf: optimize debug_storageRangeAt
#809
Conversation
| get(key: LargeNumber, cb: Callback<Buffer | null>): void; | ||
| put(key: LargeNumber, value: LargeNumber, cb: Callback<never>): void; | ||
| copy(): Trie; | ||
| copy(): CheckpointTrie; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just fixing some types
| @@ -0,0 +1,11 @@ | |||
| declare module "merkle-patricia-tree/secure" { | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just adding a type for SecureTrie
| if (txHashCurrentlyProcessing == transactionHash) { | ||
| const beforeTxListener = (tx: Transaction) => { | ||
| txHashCurrentlyProcessing = tx.hash(); | ||
| if (txHashCurrentlyProcessing.equals(transactionHash)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comparing buffers is faster than converting them to a string then comparing strings.
| (err: Error, result: Buffer) => { | ||
| if (err) { | ||
| throw err; | ||
| const database = this.#database; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For reasons, this.#database is slow once typescript compiles things, so I like to cache the lookup.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How did you measure the performance difference @davidmurdoch ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't need to. Just look at what typescript generates for our ES target. It's a function call, a Map has call, a branch, and a Map get call, IIRC.
I'd love to generate multiple builds so we can ship without these sorts of polyfills. I like the "true" private fields the syntax provides, and don't think I want to remove their use all together.
Happy to have your review on this!
| if (err) { | ||
| throw err; | ||
| const database = this.#database; | ||
| return Promise.all( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The beforeTx event emitter awaits the returned promise. I didn't find any race condition issues in our tests, but this seemed like a place they could crop up, so I thought it'd be good to "fix" it anyway.
|
|
||
| resolve(keys); | ||
| }); | ||
| const rs = storageTrie.createReadStream(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can't get keys to come back already ordered because the merkle patricia tree library we use parallelizes up to 500 database requests at once.
This just means we have to sort client side, which could mean performance takes a hit.
But generally I think callers to debug_storageRangeAt will attempt to consume all available storage in one go, which means we would have had to load all the data anyway.
Where we'd take a hit is when we only need to return a small amount of storage. In that case we still have to load all of it.
I think turning the priority queue it uses internally into a FIFO queue would actually be very complicated, because it resolves both branches and leaves, and it'd have to ensure the proper ordering there. Not positive though.
Since that's not going to happen any time soon, the next best thing is to just insert them in order as we get them, instead of a big oll sort at the end.
This will suck a bit more after we get the in-memory cache in place. We won't need any parallelization in that case; and it'll actually just slow everything down (it'll still be faster than hitting leveldb, of course).
Thanks for watching. Don't forget to smash that like button!
| #initialize = async () => { | ||
| const levelupOptions: any = { valueEncoding: "binary" }; | ||
| const levelupOptions: any = { | ||
| keyEncoding: "binary", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This makes it so that we don't have to stringify our keys, which are almost always, if not always, already a Buffer.
| storageKeys.set(hashedKey, key.toBuffer()); | ||
| ).toBuffer(); | ||
| const hashedKey = keccak(key); | ||
| storageKeys.set(hashedKey.toString(), { key, hashedKey }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Trading some memory for CPU. Previously the consumer of this data would convert the Map's keys (hashedKey.toString()) back into Buffers before saving them to the database. Now it just reads the Buffers stored in the values of the Map.
| export function findInsertPosition<T>( | ||
| array: T[], | ||
| val: T, | ||
| comp: (a: T, b: T) => boolean | ||
| ) { | ||
| let count = array.length; | ||
|
|
||
| let first = 0; | ||
| let offset = 0; | ||
| while (count > 0) { | ||
| let step = (count / 2) | 0; | ||
| offset += step; | ||
| if (!comp(val, array[offset])) { | ||
| first = ++offset; | ||
| count -= step + 1; | ||
| } else { | ||
| count = step; | ||
| offset = first; | ||
| } | ||
| } | ||
| return first; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Binary searches a sorted array for the sorted insert position.
This implementation was ported from the C++ stdlib, http://www.cplusplus.com/reference/algorithm/upper_bound/.
debug_storageRangeAtdebug_storageRangeAt
eshaben
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just a couple small things... looks good though! thanks for cleaning it up... keyStart 😆
Co-authored-by: Erin Shaben <[email protected]>
eshaben
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
gnidan
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here's a couple style suggestions. This looks good!
Co-authored-by: g. nicholas d'andrea <[email protected]>
Co-authored-by: Erin Shaben <[email protected]> Co-authored-by: g. nicholas d'andrea <[email protected]>
No description provided.