-
-
Notifications
You must be signed in to change notification settings - Fork 289
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
Decrypt keystores in a thread pool #5357
Conversation
Performance Report✔️ no performance regression detected Full benchmark results
|
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.
Overall looks good, some minor comments
packages/cli/src/cmds/validator/keymanager/decryptKeystoreDefinitions/index.ts
Outdated
Show resolved
Hide resolved
packages/cli/src/cmds/validator/keymanager/decryptKeystoreDefinitions/index.ts
Outdated
Show resolved
Hide resolved
the results importing 2000 keys on
unstable: 19.7 keys/m
with transferable objects: 100.5 keys/m
without transferable objects: 102.2 keys/m
|
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.
Issues found during testing
- incorrect initial log
info: 100% of keystores imported. current=0 total=2000 rate=0.00keys/m
, should show 0 instead of 100% - keystore cache does not seem to work, the file is created after import but on next startup is removed and keys are loaded from scratch
I think we can do better than 1 decryption per thread at a time. ChatGPT was helpful here: -- The memory usage of Scrypt depends on the specific parameters chosen. These parameters include:
The memory usage can be roughly calculated with the following formula: Memory Usage = 128 * N * r * p bytes For example, using the parameters N=2^14 (16,384), r=8, and p=1, the memory usage would be: Memory Usage = 128 * 16,384 * 8 * 1 = 16,777,216 bytes, or approximately 16 MB. -- The default used by the deposit cli is here Comes out to ~256MB. So since each thread has its own heap, we can really do quite a bit more decryptions at a time. Ofc taking into account (conservatively) the total amount of free memory available ( I'd like to see what this looks like and add some benchmarks here so we can keep track of this. |
👀 will look into that |
this happens due to the fact that
It seems like the problem is just how we calculate the percentage here
if current=0 (which is the default) then this will always result in 100 due to 0 being falsy > 0 && 2000 ? (0 / 2000) * 100 : 100
100 |
packages/cli/src/cmds/validator/keymanager/decryptKeystoreDefinitions/index.ts
Show resolved
Hide resolved
packages/cli/src/cmds/validator/keymanager/decryptKeystoreDefinitions/index.ts
Show resolved
Hide resolved
packages/cli/src/cmds/validator/keymanager/decryptKeystoreDefinitions/worker.ts
Show resolved
Hide resolved
packages/cli/src/cmds/validator/keymanager/decryptKeystoreDefinitions/index.ts
Outdated
Show resolved
Hide resolved
packages/cli/src/cmds/validator/keymanager/decryptKeystoreDefinitions/types.ts
Outdated
Show resolved
Hide resolved
I believe there was a test for this case, if missed we should add it. |
@nflaig curious to see what your benchmark shows now after the latest commit |
It does not look great on my machine (or at least not better than before), it is overloaded which I think just causes bad overall performance (note: I am also running geth and several lodestar instance on that host) This is the result of
Those took around 30GB of RAM and put all CPUs under full load (also happened before) as you can see above, probably need to adjust the params somehow to be a bit more conservative. Just as reference, It does look like memory utilization is better but does not seem to improve performance, I think CPU is just the bottleneck? This is the log output from latest commit, it looks like decrypt batches take too long now
Some logs show negative rate for keys/m, likely related to how
Problems mentioned earlier are fixed now
|
packages/cli/src/cmds/validator/keymanager/decryptKeystoreDefinitions/types.ts
Show resolved
Hide resolved
Ok will revert the last commit so we just have a job concurrency of 1.
Lets keep the top-level CLI flag for now (so as to not introduce a breaking change to users) and just use And explicitly assign |
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, just few final remarks after more in-depth review. Everything works as expected now and the performance boost is pretty amazing, on average it is now 5x faster and in combination with the keystore cache I think the UX for big operators is pretty good now
const pool = Pool( | ||
() => | ||
spawn<DecryptKeystoreWorkerAPI>(new Worker("./worker.js"), { | ||
// A Lodestar Node may do very expensive task at start blocking the event loop and causing |
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 comment seems a bit out of place, should probably be revised. In case of loading the keys there is really nothing else going on as this is a blocking operation
packages/cli/src/cmds/validator/keymanager/decryptKeystoreDefinitions/index.ts
Outdated
Show resolved
Hide resolved
if (typeof navigator !== "undefined") { | ||
defaultPoolSize = navigator.hardwareConcurrency ?? 4; | ||
} else { | ||
defaultPoolSize = (await import("node:os")).cpus().length; |
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.
noting here that we should change this line to use os.availableParallelism() once we upgrade to node v20
/** | ||
* Decrypt a single keystore definition, returning the secret key as a Uint8Array | ||
* | ||
* NOTE: This is memory-intensive process, since decrypting the keystore involves running a key derivation function (either pbkdf2 or scrypt) |
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.
packages/cli/test/unit/validator/decryptKeystoreDefinitions.test.ts
Outdated
Show resolved
Hide resolved
Co-authored-by: Nico Flaig <nflaig@protonmail.com>
@wemeetagain I noticed that we have a similar logic in the keymanager API. Is supporting thread pool decryption something we should consider there as well or do we expect that this API will generally be used to import less overall keys?
|
Probably wouldn't hurt to support decryption in parallel here. 🤷 |
Yes definitely, that should be done in a thread and limit with a queue to prevent killing the vc process |
🎉 This PR is included in v1.8.0 🎉 |
Motivation
Resolves #4179
Description
decryptKeystoreDefinitions
function