-
Notifications
You must be signed in to change notification settings - Fork 386
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
gnovm/pkg/gnolang: the use of maps for Attributes.data causes a huge memory bloat and lots of calls to internal/runtime/maps.newarray along with lots of garbage collection #3436
Labels
🐞 bug
Something isn't working
Comments
odeke-em
added a commit
to odeke-em/gno
that referenced
this issue
Jan 2, 2025
…mance Noticed in profiling stdlibs/bytes that a ton of memory was being used in maps, and that's due to the conventional CS 101 that maps with O(1) lookups, insertions and deletions beat O(n) slices' performance, but when n is small, the memory bloat is not worth it and we can use slices as evidenced in profiles for which there was 30% perceptible reduction in RAM where * Before: ```shell Showing nodes accounting for 92.90MB, 83.87% of 110.76MB total Dropped 51 nodes (cum <= 0.55MB) Showing top 10 nodes out of 123 flat flat% sum% cum cum% 47.37MB 42.77% 42.77% 47.37MB 42.77% internal/runtime/maps.newarray 10.50MB 9.48% 52.25% 10.50MB 9.48% internal/runtime/maps.NewEmptyMap 8MB 7.22% 59.47% 8MB 7.22% github.com/gnolang/gno/gnovm/pkg/gnolang.(*StaticBlock).InitStaticBlock 7.51MB 6.78% 66.25% 13.03MB 11.76% github.com/gnolang/gno/gnovm/pkg/gnolang.Go2Gno 6.02MB 5.43% 71.68% 10.73MB 9.68% github.com/gnolang/gno/gnovm/pkg/gnolang.(*defaultStore).SetObject 4MB 3.61% 75.29% 4MB 3.61% github.com/gnolang/gno/gnovm/pkg/gnolang.NewBlock 3.47MB 3.13% 78.43% 3.47MB 3.13% github.com/gnolang/gno/gnovm/pkg/gnolang.(*Allocator).NewDataArray 2.52MB 2.27% 80.70% 3.52MB 3.18% github.com/gnolang/gno/gnovm/pkg/gnolang.toKeyValueExprs 2MB 1.81% 82.51% 2MB 1.81% runtime.allocm 1.51MB 1.36% 83.87% 1.51MB 1.36% runtime/pprof.(*profMap).lookup ``` ```shell Showing nodes accounting for 47.37MB, 42.77% of 110.76MB total ----------------------------------------------------------+------------- flat flat% sum% cum cum% calls calls% + context ----------------------------------------------------------+------------- 47.37MB 100% | internal/runtime/maps.newGroups 47.37MB 42.77% 42.77% 47.37MB 42.77% | internal/runtime/maps.newarray ----------------------------------------------------------+------------- 32.01MB 78.05% | github.com/gnolang/gno/gnovm/pkg/gnolang.preprocess1.func1 7MB 17.07% | github.com/gnolang/gno/gnovm/pkg/gnolang.evalConst (inline) 1.50MB 3.66% | github.com/gnolang/gno/gnovm/pkg/gnolang.constType (inline) 0.50MB 1.22% | github.com/gnolang/gno/gnovm/pkg/gnolang.tryPredefine.func1 0 0% 42.77% 41.01MB 37.03% | github.com/gnolang/gno/gnovm/pkg/gnolang.(*Attributes).SetAttribute 41.01MB 100% | runtime.mapassign_faststr ----------------------------------------------------------+------------- 4.50MB 100% | github.com/gnolang/gno/gnovm/pkg/test.(*TestOptions).runTestFiles 0 0% 42.77% 4.50MB 4.06% | github.com/gnolang/gno/gnovm/pkg/gnolang.(*Machine).RunFiles 4.50MB 100% | github.com/gnolang/gno/gnovm/pkg/gnolang.(*Machine).runFileDecls ``` and after: ```shell Showing nodes accounting for 61.99MB, 73.12% of 84.78MB total Showing top 10 nodes out of 196 flat flat% sum% cum cum% 19.50MB 23.00% 23.00% 19.50MB 23.00% github.com/gnolang/gno/gnovm/pkg/gnolang.(*Attributes).SetAttribute 12.52MB 14.76% 37.77% 18.02MB 21.26% github.com/gnolang/gno/gnovm/pkg/gnolang.Go2Gno 7.58MB 8.94% 46.70% 9.15MB 10.79% github.com/gnolang/gno/gnovm/pkg/gnolang.(*defaultStore).SetObject 5MB 5.90% 52.60% 5MB 5.90% github.com/gnolang/gno/gnovm/pkg/gnolang.(*StaticBlock).InitStaticBlock 3.47MB 4.09% 56.69% 3.47MB 4.09% github.com/gnolang/gno/gnovm/pkg/gnolang.(*Allocator).NewDataArray 3MB 3.54% 60.24% 3MB 3.54% github.com/gnolang/gno/gnovm/pkg/gnolang.NewBlock 3MB 3.54% 63.77% 3MB 3.54% github.com/gnolang/gno/gnovm/pkg/gnolang.Nx (inline) 2.77MB 3.26% 67.04% 2.77MB 3.26% bytes.growSlice 2.65MB 3.12% 70.16% 2.65MB 3.12% internal/runtime/maps.newarray 2.50MB 2.95% 73.12% 2.50MB 2.95% runtime.allocm ``` Fixes gnolang#3436
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
To reduce memory bloat of the gnovm I've been examining design patterns and one struct out for gnolang.Attributes
gno/gnovm/pkg/gnolang/nodes.go
Lines 164 to 169 in beb48e7
and see
gno/gnovm/pkg/gnolang/nodes.go
Lines 206 to 211 in beb48e7
Seems like Attributes needs fast lookups hence opted for O(1) lookups with maps. However, it seems like very few attributes are set per Attributes object and with the use of maps, a ton of memory bloat which is visible when I profile the code from Gno libraries code in #3435.
Top 10 memory consumers
and
42.77% of the memory bloat spent for the top 10!
Remedies
I believe that we got hit here by innocent thinking but by a naive design choice that resulted in a premature optimization. Maps should be used only when there are so many attributes, for the most part O(n) linear datastructures+lookups beat O(1) data structures that require lots of memory and greatly affect garbage collection.
Preliminary test
When I apply the wisdom, turning Attributes.data into a slice and then using a loop to find values, we get a massive reduction in RAM usage by more than 30% and map creation is no longer in the top 5: it drops from 47.37MB down to 2.50MB! per
Kindly cc-ing @notJoon @jaekwon @thehowl @petar-dambovaliev
The text was updated successfully, but these errors were encountered: