-
Notifications
You must be signed in to change notification settings - Fork 1.4k
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
More reduced allocations #7193
base: main
Are you sure you want to change the base?
More reduced allocations #7193
Conversation
6809980
to
5c3f996
Compare
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.
Thanks for working on this. Some comments inline 🙃
s.Foreach(func(x *Term) { | ||
if !other.Contains(x) { | ||
r.Add(x) | ||
terms := make([]*Term, 0, len(s.keys)) |
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.
Overallocating is better than re-allocating, I suppose? 🤔
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.
Not sure tbh, an the length can probably be tweaked. Rewriting these was not a huge allocation saver, but it did save some and performed consistently better across all tests, so I left it be.
topdown/walk.go
Outdated
if filter == nil || filter.Len() == 0 { | ||
if path == nil { | ||
path = ast.NewArray() | ||
} | ||
|
||
if err := iter(ast.ArrayTerm(ast.NewTerm(path.Copy()), input)); err != nil { | ||
// TODO: why does this not 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.
TODO: let's find an answer before merging this 😉
I think it doesn't work because defer doesn't go well with CPS. return iter(...)
will mean that the defer runs when everything is evaluated successfully.
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.
let's find an answer before merging this
Naturally :) I'll see if I can get something to work without defer ... but note that this actually does work in the walkNoPath version... the few added unit tests on this confirms so, but even more so that regal lint
works, as we're quite dependent on that.
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.
I think it doesn't work because defer doesn't go well with CPS. return iter(...) will mean that the defer runs when everything is evaluated successfully.
This seems to align with what some println's tell me. However, I'm not sure why this would be an issue. Even if all path/value pairs aren't returned to the pool until after the whole walk is done, that would still be a substantial improvement over having to create all new items the next walk is invoked. BUT the YAML tests show some really odd behavior in the printed path/values vs the expected ones! But now that I try to run these changes using regal lint bundle
, everything actually works.. and we use walk (even with path) quite extensively... so I'm really really clueless as to what's going on right now 😅
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.
@srenatus I have now pushed my latest changes, that return items to the pool "manually" instead of via defer. I also added a pool for terms as they too can be reduced, and all in all this is another 4 million allocations down if successful. The OPA YAML tests related to walk are failing, where Regal just keeps going. I don't even know where to start 😄 But perhaps you'll come up with some ideas. I figured I'd push this so that we're on the same page.
d6102d8
to
409425a
Compare
The failing test is "unrelated to this PR", which is a bit of a bold statement, given that the test was added by this very PR. But that test fails even on current OPA, and it seems like we have some ordering issue in the Wasm-implementation of
|
|
||
termPool = sync.Pool{ | ||
New: func() any { | ||
return ast.NewTerm(ast.Boolean(false)) |
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.
I think I'd be less surprised about something like return &ast.Term{}
-- why bother with a Value if it's not supposed to have one in the first place?
2a7b437
to
49d325c
Compare
For the first time, down under a 100 million allocations when running `regal lint bundle` 🎈 **main** ``` BenchmarkLintAllEnabled-10 1 2538350916 ns/op 6182626816 B/op 108424249 allocs/op ``` **pr** ``` BenchmarkLintAllEnabled-10 1 2282894416 ns/op 5310032744 B/op 93674054 allocs/op ``` But there's more to it than just the number of allocations: ``` ➜ hyperfine -i --warmup 1 'regal lint bundle' 'regal-new lint bundle' Benchmark 1: regal lint bundle Time (mean ± σ): 2.822 s ± 0.055 s [User: 19.299 s, System: 0.603 s] Range (min … max): 2.743 s … 2.961 s 10 runs Benchmark 2: regal-new lint bundle Time (mean ± σ): 2.373 s ± 0.040 s [User: 15.940 s, System: 0.575 s] Range (min … max): 2.315 s … 2.435 s 10 runs Summary regal-new lint bundle ran 1.19 ± 0.03 times faster than regal lint bundle ``` Most notable changes: - Reuse trieTraversalResult in indexing, as these were expensive and short-lived. This had the most dramatic impact on the number of reduced allocations of all the changes here. - Optimize *set, *object and *Array operations to minimize allocations by using "primitive" form iteration instead of the function literal counterparts internally, and to only reset the sort guard when needed. - New Array.Equal implementation does not remove any allocations as the old implementation didn't allocate either. It did however perform much better for the case where the compared arrays were not equal. Signed-off-by: Anders Eknert <[email protected]>
49d325c
to
4206ed7
Compare
For the first time, down under a 100 million allocations when running
regal lint bundle
🎈main
pr
But there's more to it than just the number of allocations:
Most notable changes:
Reuse trieTraversalResult in indexing, as these were expensive and short-lived. This had the most dramatic impact on the number of reduced allocations of all the changes here.
Optimize *set, *object and *Array operations to minimize allocations by using "primitive" form iteration instead of the function literal counterparts internally, and to only reset the sort guard when needed.
New Array.Equal implementation does not remove any allocations as the old implementation didn't allocate either. It did however perform much better for the case where the compared arrays were not equal.