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

chore: optimize maps.Values and maps.Keys #17

Merged
merged 1 commit into from
May 20, 2024

Conversation

DmitriyMV
Copy link
Member

This PR optimizes the performance of maps.Values and maps.Keys (around 125 usages in sidero code) by using the internal runtime functions runtime.keys and runtime.values. There is strong desire in Go Team to disable go:linkname usage in user code 1, but this is a special case where we use Push linkname pattern which is said to be supported in the future. It is tested with both Go 1.22 and latest Go with CL 585556 2 applied.

To futher future-proof this code, we have added a build tag go1.22 && !go1.24 to ensure this code is only compiled with Go 1.22 and 1.23 and falls back to the old implementation for Go 1.24 and above. This is because we don't know yet if runtime.keys and runtime.values are going to be present in Go 1.24. We will update this code when Go 1.24 freeze happens.

Benchstat results (overall 26% CPU usage reduction):

~ benchstat old.txt new.txt
goos: darwin
goarch: arm64
pkg: github.com/siderolabs/gen/maps
                │   old.txt    │               new.txt               │
                │    sec/op    │   sec/op     vs base                │
Keys/small-10     111.05n ± 0%   80.74n ± 3%  -27.29% (p=0.000 n=10)
Keys/mid-10        837.8n ± 1%   607.8n ± 2%  -27.46% (p=0.000 n=10)
Keys/large-10      7.717µ ± 4%   5.711µ ± 0%  -26.00% (p=0.000 n=10)
Values/small-10   110.40n ± 0%   83.69n ± 0%  -24.19% (p=0.000 n=10)
Values/mid-10      835.9n ± 1%   583.5n ± 1%  -30.19% (p=0.000 n=10)
Values/large-10    7.720µ ± 2%   5.696µ ± 1%  -26.22% (p=0.000 n=10)
geomean            894.3n        653.6n       -26.92%

                │   old.txt    │                new.txt                │
                │     B/op     │     B/op      vs base                 │
Keys/small-10       80.00 ± 0%     80.00 ± 0%       ~ (p=1.000 n=10) ¹
Keys/mid-10         896.0 ± 0%     896.0 ± 0%       ~ (p=1.000 n=10) ¹
Keys/large-10     8.000Ki ± 0%   8.000Ki ± 0%       ~ (p=1.000 n=10) ¹
Values/small-10     80.00 ± 0%     80.00 ± 0%       ~ (p=1.000 n=10) ¹
Values/mid-10       896.0 ± 0%     896.0 ± 0%       ~ (p=1.000 n=10) ¹
Values/large-10   8.000Ki ± 0%   8.000Ki ± 0%       ~ (p=1.000 n=10) ¹
geomean             837.4          837.4       +0.00%
¹ all samples are equal

                │  old.txt     │                new.txt                │
                │ allocs/op    │  allocs/op    vs base                 │
Keys/small-10     1.000 ± 0%     1.000 ± 0%         ~ (p=1.000 n=10) ¹
Keys/mid-10       1.000 ± 0%     1.000 ± 0%         ~ (p=1.000 n=10) ¹
Keys/large-10     1.000 ± 0%     1.000 ± 0%         ~ (p=1.000 n=10) ¹
Values/small-10   1.000 ± 0%     1.000 ± 0%         ~ (p=1.000 n=10) ¹
Values/mid-10     1.000 ± 0%     1.000 ± 0%         ~ (p=1.000 n=10) ¹
Values/large-10   1.000 ± 0%     1.000 ± 0%         ~ (p=1.000 n=10) ¹
geomean           1.000          1.000         +0.00%
¹ all samples are equal

Footnotes

  1. https://github.com/golang/go/issues/67401

  2. https://go-review.googlesource.com/c/go/+/585556

This PR optimizes the performance of maps.Values and maps.Keys (around 125 usages in sidero code)
by using the internal runtime functions runtime.keys and runtime.values. There is strong desire in Go Team to disable
`go:linkname` usage in user code [^1], but this is a special case where we use `Push` `linkname` pattern which is said
to be supported in the future. It is tested with both Go 1.22 and latest Go with CL 585556 [^2] applied.

To further future-proof this code, we have added a build tag `go1.22 && !go1.24` to ensure this code is only compiled
with Go 1.22 and 1.23 and falls back to the old implementation for Go 1.24 and above. This is because we don't know yet
if `runtime.keys` and `runtime.values` are going to be present in Go 1.24. We will update this code when Go 1.24 freeze
happens.

Benchstat results below (overall 26% CPU usage reduction):
```bash
~ benchstat old.txt new.txt
goos: darwin
goarch: arm64
pkg: github.com/siderolabs/gen/maps
                │   old.txt    │               new.txt               │
                │    sec/op    │   sec/op     vs base                │
Keys/small-10     111.05n ± 0%   80.74n ± 3%  -27.29% (p=0.000 n=10)
Keys/mid-10        837.8n ± 1%   607.8n ± 2%  -27.46% (p=0.000 n=10)
Keys/large-10      7.717µ ± 4%   5.711µ ± 0%  -26.00% (p=0.000 n=10)
Values/small-10   110.40n ± 0%   83.69n ± 0%  -24.19% (p=0.000 n=10)
Values/mid-10      835.9n ± 1%   583.5n ± 1%  -30.19% (p=0.000 n=10)
Values/large-10    7.720µ ± 2%   5.696µ ± 1%  -26.22% (p=0.000 n=10)
geomean            894.3n        653.6n       -26.92%

                │   old.txt    │                new.txt                │
                │     B/op     │     B/op      vs base                 │
Keys/small-10       80.00 ± 0%     80.00 ± 0%       ~ (p=1.000 n=10) ¹
Keys/mid-10         896.0 ± 0%     896.0 ± 0%       ~ (p=1.000 n=10) ¹
Keys/large-10     8.000Ki ± 0%   8.000Ki ± 0%       ~ (p=1.000 n=10) ¹
Values/small-10     80.00 ± 0%     80.00 ± 0%       ~ (p=1.000 n=10) ¹
Values/mid-10       896.0 ± 0%     896.0 ± 0%       ~ (p=1.000 n=10) ¹
Values/large-10   8.000Ki ± 0%   8.000Ki ± 0%       ~ (p=1.000 n=10) ¹
geomean             837.4          837.4       +0.00%
¹ all samples are equal

                │  old.txt     │                new.txt                │
                │ allocs/op    │  allocs/op    vs base                 │
Keys/small-10     1.000 ± 0%     1.000 ± 0%         ~ (p=1.000 n=10) ¹
Keys/mid-10       1.000 ± 0%     1.000 ± 0%         ~ (p=1.000 n=10) ¹
Keys/large-10     1.000 ± 0%     1.000 ± 0%         ~ (p=1.000 n=10) ¹
Values/small-10   1.000 ± 0%     1.000 ± 0%         ~ (p=1.000 n=10) ¹
Values/mid-10     1.000 ± 0%     1.000 ± 0%         ~ (p=1.000 n=10) ¹
Values/large-10   1.000 ± 0%     1.000 ± 0%         ~ (p=1.000 n=10) ¹
geomean           1.000          1.000         +0.00%
¹ all samples are equal
```

[^1]: golang/go#67401
[^2]: https://go-review.googlesource.com/c/go/+/585556

Signed-off-by: Dmitriy Matrenichev <dmitry.matrenichev@siderolabs.com>
@DmitriyMV DmitriyMV force-pushed the maps-optimizations branch from 622f803 to 8485864 Compare May 19, 2024 21:06
@DmitriyMV
Copy link
Member Author

/m

@talos-bot talos-bot merged commit 8485864 into siderolabs:main May 20, 2024
14 checks passed
@DmitriyMV DmitriyMV deleted the maps-optimizations branch October 31, 2024 19:22
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.

4 participants