-
Notifications
You must be signed in to change notification settings - Fork 112
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
Optimize Peer lookups #447
Merged
Merged
Conversation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
I don't know if there was an intention behind lazily initializing the Peer struct on-demand within the Peer() method, but from what I can tell, Peer() ends up being called 100% of the time in every path I've seen, and within NewClient, it's ultimately called twice. Within NewClient, we also parse the params.URL then multiple times through url.ParseRequestURI then again through url.Parse, which is rather expensive and requires at least 1 heap allocation for the url.URL struct which we discard. This behavior changes so the URL itself is parsed 1 time, then pass the url.URL struct to both functions that need it. As well as memoize the actual Peer on our concrete Client struct. There is a slight change in behavior, but from my understanding, this seems safe: validateRequestURL runs through url.ParseRequestURI, and newPeerFromURL was passed through url.Parse instead. From my understanding and my testing, url.ParseRequestURI is more strict than url.Parse, but since validateRequestURL was always called first, I don't particularly see how a less strict URL could have gotten through to the subsequent call to create the Peer.
Makes sense to me. I think I had some vague thought that I pushed a commit that inlines the grpc-specific NewPeer function, but that's just a taste thing - code's great. ❤️ |
akshayjshah
approved these changes
Jan 28, 2023
renovate bot
referenced
this pull request
in open-feature/flagd
Feb 2, 2023
[](https://renovatebot.com) This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [github.com/bufbuild/connect-go](https://togithub.com/bufbuild/connect-go) | require | patch | `v1.5.0` -> `v1.5.1` | --- ### Release Notes <details> <summary>bufbuild/connect-go</summary> ### [`v1.5.1`](https://togithub.com/bufbuild/connect-go/releases/tag/v1.5.1) [Compare Source](https://togithub.com/bufbuild/connect-go/compare/v1.5.0...v1.5.1) <!-- Release notes generated using configuration in .github/release.yml at main --> #### What's Changed Thanks to [@​mattrobenolt](https://togithub.com/mattrobenolt), v1.5.1 exclusively contains performance improvements. There should be no other user-visible behavior changes. ##### Bugfixes - Minimize allocations writing User-Agent header by [@​mattrobenolt](https://togithub.com/mattrobenolt) in [https://github.com/bufbuild/connect-go/pull/446](https://togithub.com/bufbuild/connect-go/pull/446) - Minimize allocations parsing Content-Type by [@​mattrobenolt](https://togithub.com/mattrobenolt) in [https://github.com/bufbuild/connect-go/pull/444](https://togithub.com/bufbuild/connect-go/pull/444) - Optimize header access by [@​mattrobenolt](https://togithub.com/mattrobenolt) in [https://github.com/bufbuild/connect-go/pull/445](https://togithub.com/bufbuild/connect-go/pull/445) - Optimize Peer lookups by [@​mattrobenolt](https://togithub.com/mattrobenolt) in [https://github.com/bufbuild/connect-go/pull/447](https://togithub.com/bufbuild/connect-go/pull/447) **Full Changelog**: bufbuild/connect-go@v1.5.0...v1.5.1 </details> --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://app.renovatebot.com/dashboard#github/open-feature/flagd). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNC4xMTkuMCIsInVwZGF0ZWRJblZlciI6IjM0LjExOS4wIn0=--> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
mattrobenolt
added a commit
to mattrobenolt/connect-go
that referenced
this pull request
Feb 18, 2023
This stood out on flame graphs due ultimately to the http.NewRequestFromContext call, which ends up calling url.Parse. I did an optimization in connectrpc#447 which similarly, and this is a natural extension of this. We can just parse the url string once during NewClient, validate at that point, then tag along the parsed *url.URL everywhere else and never use it as a string again. This value never mutates through the lifetime of a Client and isn't publicly available on any structs.
mattrobenolt
added a commit
to mattrobenolt/connect-go
that referenced
this pull request
Feb 18, 2023
This stood out on flame graphs due ultimately to the http.NewRequestFromContext call, which ends up calling url.Parse. I did an optimization in connectrpc#447 which similarly, and this is a natural extension of this. We can just parse the url string once during NewClient, validate at that point, then tag along the parsed *url.URL everywhere else and never use it as a string again. This value never mutates through the lifetime of a Client and isn't publicly available on any structs.
akshayjshah
pushed a commit
that referenced
this pull request
Mar 6, 2023
This stood out on flame graphs due ultimately to the http.NewRequestFromContext call, which ends up calling url.Parse and this happens on every single RPC call. I did an optimization in #447 which similarly, and this is a natural extension of this. We can just parse the url string once during NewClient, validate at that point, then tag along the parsed *url.URL everywhere else and never use it as a string again. This value never mutates through the lifetime of a Client and isn't publicly available on any structs. Before: <img width="1026" alt="image" src="https://user-images.githubusercontent.com/375744/219882639-b05416ee-3a8f-4b38-9716-9d66a34eeffc.png"> After: <img width="1177" alt="image" src="https://user-images.githubusercontent.com/375744/220477453-c1b48350-665b-495b-a070-3efb08e53067.png">
xxgreg
pushed a commit
to xxgreg/connect-go
that referenced
this pull request
Apr 14, 2023
xxgreg
pushed a commit
to xxgreg/connect-go
that referenced
this pull request
Apr 14, 2023
akshayjshah
added a commit
that referenced
this pull request
Jul 26, 2023
Peer() is nearly always called, often more than once. Because Peer is a returned as a value and all fields are immutable strings, it's safe to create it once and memoize it. Along the way, this PR also cuts down on repeated URL parsing. --------- Co-authored-by: Akshay Shah <ashah@buf.build>
akshayjshah
pushed a commit
that referenced
this pull request
Jul 26, 2023
This stood out on flame graphs due ultimately to the http.NewRequestFromContext call, which ends up calling url.Parse and this happens on every single RPC call. I did an optimization in #447 which similarly, and this is a natural extension of this. We can just parse the url string once during NewClient, validate at that point, then tag along the parsed *url.URL everywhere else and never use it as a string again. This value never mutates through the lifetime of a Client and isn't publicly available on any structs. Before: <img width="1026" alt="image" src="https://user-images.githubusercontent.com/375744/219882639-b05416ee-3a8f-4b38-9716-9d66a34eeffc.png"> After: <img width="1177" alt="image" src="https://user-images.githubusercontent.com/375744/220477453-c1b48350-665b-495b-a070-3efb08e53067.png">
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
I don't know if there was an intention behind lazily initializing the Peer struct on-demand within the Peer() method, but from what I can tell, Peer() ends up being called 100% of the time in every path I've seen, and within NewClient, it's ultimately called twice.
Within NewClient, we also parse the params.URL then multiple times through url.ParseRequestURI then again through url.Parse, which is rather expensive and requires at least 1 heap allocation for the url.URL struct which we discard.
This behavior changes so the URL itself is parsed 1 time, then pass the url.URL struct to both functions that need it. As well as memoize the actual Peer on our concrete Client struct.
There is a slight change in behavior, but from my understanding, this seems safe:
validateRequestURL runs through url.ParseRequestURI, and newPeerFromURL was passed through url.Parse instead. From my understanding and my testing, url.ParseRequestURI is more strict than url.Parse, but since validateRequestURL was always called first, I don't particularly see how a less strict URL could have gotten through to the subsequent call to create the Peer.
Before submitting your PR: Please read through the contribution guide at https://github.com/bufbuild/connect-go/blob/main/.github/CONTRIBUTING.md