-
Notifications
You must be signed in to change notification settings - Fork 180
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
[EN Performance] Reduce memory used for ledger.Payload by 32+ GB, eliminate 1+ billion allocs/op, speedup various ops #2930
Conversation
Moved common/encoding/encoding.go from encoding package to ledger package. Split common/utils/testutils.go into two files. Common utility functions are in common/utils/utils.go. Common test utility functions such as creating fixtures are in common/testutils/testutils.go. This is needed for an upcoming commit that needs to use encoded key in Payload (which requires encoding functions).
ledger.Payload is immutable value object with key and value fields. This commit is needed by upcoming commit that will use encoded payload key in the key field.
Reduce data held in RAM by dozens of GB, reduce allocs, and improve speed of: ledger update, checkpoint serialization, and rebuilding Mtrie at startup by: - keeping mtrie leaf node's payload key encoded in memory - using encoded key directly while serializing checkpoint/WAL/TrieProof - using encoded key directly while deserializing checkpoint/WAL/TrieProof Benchmark is only for TrieUpdate. Other improvements are not benchmarked yet. name old time/op new time/op delta TrieUpdate-4 439ms ± 2% 409ms ± 1% -6.94% (p=0.000 n=18+20) name old alloc/op new alloc/op delta TrieUpdate-4 73.5MB ± 0% 34.1MB ± 0% -53.60% (p=0.000 n=20+20) name old allocs/op new allocs/op delta TrieUpdate-4 187k ± 0% 147k ± 0% -21.44% (p=0.000 n=20+20)
owner := k.KeyParts[0].Value | ||
key := k.KeyParts[2].Value |
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.
Is there some inherent structure to KeyParts? Maybe we can convert it to either a Struct or an Array to remove even more dynamic allocations?
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.
Is there some inherent structure to KeyParts? Maybe we can convert it to either a Struct or an Array to remove even more dynamic allocations?
You're right, the structure of ledger.Key
can be optimized to reduce number of allocs but it probably won't be worth the effort to do that after I eliminate use of ledger.Key
to just migration and reporting.
Ledger key is designed to be flexible, so it can contain variable number of KeyPart
. But we have plans to limit uses of Ledger key to migration and reports so that we can reduce number of heap allocs.
This PR eliminates ledger.Key
from mtrie leaf nodes' payload.
My next PR related to ledger.Key
is to eliminate it being created outside of migration and reporting which would reduce number of heap allocs caused by ledger.Key
.
EDIT: add a one-sentence summary to the beginning of my reply and highlight variable names.
ledger.Payload's fields are unexported, so custom JSON and CBOR encoding/decoding are needed.
Codecov Report
@@ Coverage Diff @@
## master #2930 +/- ##
==========================================
- Coverage 57.16% 54.73% -2.43%
==========================================
Files 693 714 +21
Lines 63112 66184 +3072
==========================================
+ Hits 36076 36227 +151
- Misses 24078 26959 +2881
- Partials 2958 2998 +40
Flags with carried forward coverage won't be shown. Click here to find out more.
Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here. |
func TestNow(t *testing.T) { | ||
|
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.
do we know what was the original reason for this test ?
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.
Yes, TestNow
was a throwaway test that wasn't meant to be pushed to the repo. I use TestNow
to verify my understanding locally and forgot to remove it in a prior PR.
// Value returns payload value. | ||
// CAUTION: do not modify returned value because it shares underlying data with payload value. | ||
func (p *Payload) Value() Value { | ||
if p == nil { | ||
return Value{} | ||
} | ||
return p.value | ||
} | ||
|
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 is great, this can be a good start for the future PRs to remove deep copies.
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.
Looks good to me, nice work.
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.
Look good. Great works! Thanks
This PR replaces decoded payload key with encoded key buffer because decoded payload key is only used for migration and reports. This change made ledger.Payload immutable.
Closes #2569
Closes #2248 (together with changes in PR #2560)
Updates #1744
Goals
Impact
Example positive side-effect beyond operational RAM reduction
Benchstats are only for ledger update. Other improvements are not benchmarked yet.
Caveats
Reviewers
This PR is fairly simple because the most important changes are in the small commit e35bd94.
The large number of lines changed by other commit is to: