The Wayback Machine - https://web.archive.org/web/20221219225903/https://github.com/golang/go/pull/57080
Skip to content
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

encoding: do not cache large encodeState #57080

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

linxiulei
Copy link

@linxiulei linxiulei commented Dec 5, 2022

It's not efficient to cache large encodeState objects (>32k) because it might build up memory usage significant in a short period of time with high concurrency.

I ran the benchmark and as expected some tests are worse because the test data contain data larger than 32k

Fix kubernetes/kubernetes#114276

Benchmark

name                                 old time/op    new time/op       delta
CodeEncoder-16                         1.15ms ± 3%       1.74ms ± 5%        +51.87%  (p=0.000 n=18+20)
CodeEncoderError-16                    1.22ms ± 2%       2.36ms ± 6%        +93.26%  (p=0.000 n=18+19)
CodeMarshal-16                         1.38ms ± 4%       2.01ms ± 6%        +45.51%  (p=0.000 n=20+19)
CodeMarshalError-16                    1.61ms ± 3%       2.69ms ± 3%        +67.02%  (p=0.000 n=17+18)
MarshalBytes/32-16                      293ns ± 1%        299ns ± 2%         +1.82%  (p=0.000 n=19+20)
MarshalBytes/256-16                     888ns ± 1%        938ns ± 3%         +5.60%  (p=0.000 n=20+19)
MarshalBytes/4096-16                   8.97µs ± 2%       9.23µs ± 3%         +2.84%  (p=0.000 n=20+20)
MarshalBytesError/32-16                 612µs ± 3%        618µs ± 2%         +0.97%  (p=0.008 n=19+19)
MarshalBytesError/256-16                613µs ± 4%        619µs ± 3%         +0.85%  (p=0.026 n=19+20)
MarshalBytesError/4096-16               628µs ± 2%        683µs ± 2%         +8.83%  (p=0.000 n=20+18)
CodeDecoder-16                         5.46ms ± 4%       5.49ms ± 4%           ~     (p=0.303 n=17+18)
UnicodeDecoder-16                       351ns ± 2%        346ns ± 1%         -1.59%  (p=0.000 n=19+20)
DecoderStream-16                        254ns ± 2%        249ns ± 1%         -1.63%  (p=0.000 n=19+20)
CodeUnmarshal-16                       6.95ms ± 3%       6.90ms ± 3%           ~     (p=0.120 n=20+19)
CodeUnmarshalReuse-16                  5.65ms ± 2%       5.73ms ± 6%           ~     (p=0.284 n=19+19)
UnmarshalString-16                      102ns ± 5%        105ns ± 5%         +2.33%  (p=0.010 n=19+20)
UnmarshalFloat64-16                    93.2ns ± 4%       99.2ns ± 6%         +6.49%  (p=0.000 n=19+19)
UnmarshalInt64-16                      81.3ns ± 7%       91.5ns ± 9%        +12.49%  (p=0.000 n=19+18)
Issue10335-16                           134ns ± 2%        142ns ± 4%         +5.71%  (p=0.000 n=18+20)
Issue34127-16                          61.1ns ± 3%       62.7ns ± 4%         +2.52%  (p=0.000 n=19+20)
Unmapped-16                             298ns ± 3%        307ns ± 4%         +3.09%  (p=0.000 n=19+20)
TypeFieldsCache/MissTypes1-16          19.5µs ± 3%       21.2µs ± 4%         +8.80%  (p=0.000 n=19+19)
TypeFieldsCache/MissTypes10-16         74.4µs ± 2%       75.8µs ± 3%         +1.89%  (p=0.000 n=19+17)
TypeFieldsCache/MissTypes100-16         281µs ± 8%        288µs ±10%           ~     (p=0.232 n=17+19)
TypeFieldsCache/MissTypes1000-16       2.53ms ± 3%       2.45ms ± 4%         -3.25%  (p=0.000 n=18+19)
TypeFieldsCache/MissTypes10000-16      21.8ms ± 4%       21.7ms ± 5%           ~     (p=0.408 n=19+18)
TypeFieldsCache/MissTypes100000-16      231ms ± 3%        230ms ± 6%           ~     (p=0.270 n=20+19)
TypeFieldsCache/MissTypes1000000-16     2.66s ±13%        2.66s ±19%           ~     (p=0.940 n=19+18)
TypeFieldsCache/HitTypes1-16           3.35ns ± 3%       4.95ns ± 2%        +47.86%  (p=0.000 n=19+16)
TypeFieldsCache/HitTypes10-16          3.54ns ± 2%       8.22ns ±53%       +131.85%  (p=0.000 n=19+20)
TypeFieldsCache/HitTypes100-16         3.32ns ± 2%       4.45ns ±13%        +34.01%  (p=0.000 n=20+20)
TypeFieldsCache/HitTypes1000-16        3.42ns ± 4%       3.43ns ± 2%           ~     (p=0.364 n=20+16)
TypeFieldsCache/HitTypes10000-16       9.02ns ± 7%       3.59ns ± 2%        -60.17%  (p=0.000 n=19+18)
TypeFieldsCache/HitTypes100000-16      9.10ns ± 5%       3.44ns ± 1%        -62.23%  (p=0.000 n=16+20)
TypeFieldsCache/HitTypes1000000-16     3.44ns ± 3%       3.29ns ± 1%         -4.49%  (p=0.000 n=20+19)
EncodeMarshaler-16                     45.3ns ± 1%       47.5ns ± 6%         +4.83%  (p=0.000 n=19+19)
EncoderEncode-16                       33.2ns ± 8%       34.7ns ± 6%         +4.57%  (p=0.009 n=20+20)
NumberIsValid-16                       23.2ns ± 1%       23.3ns ± 1%           ~     (p=0.475 n=17+19)
NumberIsValidRegexp-16                  438ns ± 2%        440ns ± 2%           ~     (p=0.384 n=20+19)

name                                 old speed      new speed         delta
CodeEncoder-16                       1.69GB/s ± 3%     1.11GB/s ± 5%        -34.12%  (p=0.000 n=18+20)
CodeEncoderError-16                  1.59GB/s ± 3%     0.82GB/s ± 6%        -48.12%  (p=0.000 n=19+19)
CodeMarshal-16                       1.40GB/s ± 4%     0.97GB/s ± 6%        -31.26%  (p=0.000 n=20+19)
CodeMarshalError-16                  1.20GB/s ± 3%     0.72GB/s ± 3%        -40.13%  (p=0.000 n=17+18)
CodeDecoder-16                        356MB/s ± 4%      353MB/s ± 4%           ~     (p=0.307 n=17+18)
UnicodeDecoder-16                    39.9MB/s ± 2%     40.5MB/s ± 1%         +1.61%  (p=0.000 n=19+20)
CodeUnmarshal-16                      279MB/s ± 3%      281MB/s ± 5%           ~     (p=0.221 n=20+20)
CodeUnmarshalReuse-16                 343MB/s ± 2%      339MB/s ± 6%           ~     (p=0.284 n=19+19)

name                                 old alloc/op   new alloc/op      delta
CodeEncoder-16                          6.20B ±77%  4194594.80B ± 0%  +67654654.84%  (p=0.000 n=20+20)
CodeEncoderError-16                      141B ± 1%     4196432B ± 0%   +2969511.46%  (p=0.000 n=16+20)
CodeMarshal-16                         1.97MB ± 3%       6.14MB ± 0%       +211.22%  (p=0.000 n=20+20)
CodeMarshalError-16                    2.04MB ± 3%       6.14MB ± 0%       +201.28%  (p=0.000 n=20+20)
CodeDecoder-16                         2.18MB ± 3%       2.18MB ± 1%           ~     (p=0.384 n=18+18)
UnicodeDecoder-16                       28.0B ± 0%        28.0B ± 0%           ~     (all equal)
DecoderStream-16                        8.00B ± 0%        8.00B ± 0%           ~     (all equal)
CodeUnmarshal-16                       3.05MB ± 0%       3.05MB ± 0%         -0.00%  (p=0.040 n=20+20)
CodeUnmarshalReuse-16                  1.72MB ± 0%       1.72MB ± 1%           ~     (p=0.678 n=17+18)
UnmarshalString-16                       160B ± 0%         160B ± 0%           ~     (all equal)
UnmarshalFloat64-16                      148B ± 0%         148B ± 0%           ~     (all equal)
UnmarshalInt64-16                        144B ± 0%         144B ± 0%           ~     (all equal)
Issue10335-16                            168B ± 0%         168B ± 0%           ~     (all equal)
Issue34127-16                           24.0B ± 0%        24.0B ± 0%           ~     (all equal)
Unmapped-16                              200B ± 0%         200B ± 0%           ~     (all equal)
EncodeMarshaler-16                      4.00B ± 0%        4.00B ± 0%           ~     (all equal)
EncoderEncode-16                        0.00B             0.00B                ~     (all equal)

name                                 old allocs/op  new allocs/op     delta
CodeEncoder-16                           0.00             18.00 ± 0%          +Inf%  (p=0.000 n=20+20)
CodeEncoderError-16                      4.00 ± 0%        26.00 ± 0%       +550.00%  (p=0.000 n=20+20)
CodeMarshal-16                           1.00 ± 0%        19.00 ± 0%      +1800.00%  (p=0.000 n=20+20)
CodeMarshalError-16                      6.00 ± 0%        27.30 ± 3%       +355.00%  (p=0.000 n=19+20)
CodeDecoder-16                          77.5k ± 0%        77.5k ± 0%           ~     (p=0.341 n=18+18)
UnicodeDecoder-16                        2.00 ± 0%         2.00 ± 0%           ~     (all equal)
DecoderStream-16                         1.00 ± 0%         1.00 ± 0%           ~     (all equal)
CodeUnmarshal-16                        92.7k ± 0%        92.7k ± 0%           ~     (all equal)
CodeUnmarshalReuse-16                   77.5k ± 0%        77.5k ± 0%           ~     (p=0.750 n=17+18)
UnmarshalString-16                       2.00 ± 0%         2.00 ± 0%           ~     (all equal)
UnmarshalFloat64-16                      2.00 ± 0%         2.00 ± 0%           ~     (all equal)
UnmarshalInt64-16                        1.00 ± 0%         1.00 ± 0%           ~     (all equal)
Issue10335-16                            3.00 ± 0%         3.00 ± 0%           ~     (all equal)
Issue34127-16                            1.00 ± 0%         1.00 ± 0%           ~     (all equal)
Unmapped-16                              4.00 ± 0%         4.00 ± 0%           ~     (all equal)
EncodeMarshaler-16                       1.00 ± 0%         1.00 ± 0%           ~     (all equal)
EncoderEncode-16                         0.00              0.00                ~     (all equal)

@gopherbot
Copy link

gopherbot commented Dec 5, 2022

This PR (HEAD: 1ffe748) has been imported to Gerrit for code review.

Please visit https://go-review.googlesource.com/c/go/+/455115 to see it.

Tip: You can toggle comments from me using the comments slash command (e.g. /comments off)
See the Wiki page for more info

@gopherbot
Copy link

gopherbot commented Dec 5, 2022

Message from Gopher Robot:

Patch Set 1:

Congratulations on opening your first change. Thank you for your contribution!

Next steps:
A maintainer will review your change and provide feedback. See
https://go.dev/doc/contribute#review for more info and tips to get your
patch through code review.

Most changes in the Go project go through a few rounds of revision. This can be
surprising to people new to the project. The careful, iterative review process
is our way of helping mentor contributors and ensuring that their contributions
have a lasting impact.

During May-July and Nov-Jan the Go project is in a code freeze, during which
little code gets reviewed or merged. If a reviewer responds with a comment like
R=go1.11 or adds a tag like "wait-release", it means that this CL will be
reviewed as part of the next development cycle. See https://go.dev/s/release
for more details.


Please don’t reply on this GitHub thread. Visit golang.org/cl/455115.
After addressing review feedback, remember to publish your drafts!

@@ -312,6 +312,14 @@ func newEncodeState() *encodeState {
return &encodeState{ptrSeen: make(map[any]struct{})}
}

func cacheEncodeState(e *encodeState) {
// caching large objects is not memory efficient.
if e.Buffer.Cap() > 32768 {
Copy link

@lavalamp lavalamp Dec 5, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some limit seems like a great idea, but how did you come up with this specific number?

Copy link
Author

@linxiulei linxiulei Dec 5, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am open to any numbers <= 1MiB, which is good enough for apiserver issue that I am trying to fix

Copy link

@lavalamp lavalamp Dec 5, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I recommend doing some kind of search process to recommend an optimal value or a tighter range. E.g. binary search through possible values to find the lowest and highest ones that fix your problem.

I suspect your particular test won't be able to narrow this down too much, you'll need a more realistic object size distribution. I'm not sure where to get that from.

Copy link

@lavalamp lavalamp Dec 5, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, I just read this code more and I agree that caching large objects is going to be horribly memory inefficient because they can be reused to store small objects, which will retain all that memory for little reason. I'm not sure where a good threshold is but it may be even smaller than this.

@lavalamp
Copy link

lavalamp commented Dec 5, 2022

I think you have compared the benchmarks backwards? the diff in the OP appears to be making everything much worse :)

@lavalamp
Copy link

lavalamp commented Dec 5, 2022

...actually maybe the benchmarks weren't backwards, in which case this threshold is too small

@gopherbot
Copy link

gopherbot commented Dec 5, 2022

Message from Joseph Tsai:

Patch Set 1:

(1 comment)


Please don’t reply on this GitHub thread. Visit golang.org/cl/455115.
After addressing review feedback, remember to publish your drafts!

@gopherbot
Copy link

gopherbot commented Dec 5, 2022

Message from Eric Lin:

Patch Set 1:

(1 comment)


Please don’t reply on this GitHub thread. Visit golang.org/cl/455115.
After addressing review feedback, remember to publish your drafts!

@gopherbot
Copy link

gopherbot commented Dec 5, 2022

Message from Joseph Tsai:

Patch Set 1:

(1 comment)


Please don’t reply on this GitHub thread. Visit golang.org/cl/455115.
After addressing review feedback, remember to publish your drafts!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

apiserver builds up high memory usage after serving a few large LIST requests
3 participants