-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Use perfect-hash in HttpUriScheme.GetKnownMethod(string) instead of trie #44096
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
Use perfect-hash in HttpUriScheme.GetKnownMethod(string) instead of trie #44096
Conversation
|
Thanks for your PR, @gfoidl. Someone from the team will get assigned to your PR shortly and we'll get it reviewed. |
src/Servers/Kestrel/Core/src/Internal/Infrastructure/HttpUtilities.cs
Outdated
Show resolved
Hide resolved
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.
Looks great. Minor changes.
You link to this PR for more info, but I think it would be worth adding some more comments to the method about what is happening.
CI has test failures that look related to this change.
src/Servers/Kestrel/Core/src/Internal/Infrastructure/HttpUtilities.cs
Outdated
Show resolved
Hide resolved
Too much got check upfront, so that HttpMethod.None was returned when it actually should be HttpMethod.Custom.
|
Cc @stephentoub as we were discussing perfect hashes. |
|
I don't know if it's hot enough to be worth the same change, but it could also go here https://github.com/dotnet/runtime/blob/1d025196ae0d0f1e0b17e2e6033234e1937d997b/src/libraries/System.Net.Http/src/System/Net/Http/HttpMethod.cs#L167 |
|
I think it'd be really valuable if all of these optimized switches could just use These one-off manual-switch-implementations are hard to maintain. |
|
Thoughts on casing? |
|
For casing...current code compares with aspnetcore/src/Servers/Kestrel/Core/src/Internal/Infrastructure/HttpUtilities.cs Line 250 in ff2148b
(so that should be fixed according the issue?) Re-run the benchmark with leaving the results@@ -257,7 +257,7 @@ internal static partial class HttpUtilities
var index = PerfectHash(value);
if (index < (uint)WordListForPerfectHashOfMethods.Length
- && WordListForPerfectHashOfMethods[index] == value
+ && value.Equals(s_wordListForPerfectHashOfMethods[key], StringComparison.OrdinalIgnoreCase)
&& index < (uint)methodsLookup.Length)
{
return (HttpMethod)methodsLookup[(int)index];
@@ -300,9 +300,10 @@ internal static partial class HttpUtilities
13, 13, 13, 13, 13, 13
};
- var c = MemoryMarshal.GetReference(str);
+ // Make upper-case to have a "case-insensitive" lookup.
+ var c = MemoryMarshal.GetReference(str) & ~0x20;
- Debug.Assert(char.IsAscii(c), "Must already be valiated");
+ Debug.Assert(char.IsAscii((char)c), "Must already be valiated");
return (uint)str.Length + associatedValues[c];
}
|
|
If we want a case-insensitive check (is anyone in the real world actually sending requests like this?), the perf gains could be kept by adding a fallback if the original algorithm resolves to It's more complicated and would make Custom slightly slower, but the vast majority of HTTP requests resolve to a known value. |
+1. For this PR, if we go ahead with this custom implementation, we should link to the Roslyn issue and note that it could be removed in the future depending upon Roslyn improvements. |
Shall I add this? |
sebastienros
left a comment
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 confirm suggested implementation is faster.
Offline discussion: Making it case insensitive can be decided and done in the future. Thanks for the improvement! |
From https://www.gnu.org/software/gperf/manual/gperf.html
|
|
Hi @iSazonov. It looks like you just commented on a closed PR. The team will most probably miss it. If you'd like to bring something important up to their attention, consider filing a new issue and add enough details to build context. |
|
|
||
| var c = MemoryMarshal.GetReference(str); | ||
|
|
||
| Debug.Assert(char.IsAscii(c), "Must already be valiated"); |
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.
s/valiated/validated
Description
In
HttpUtilities.GetKnownMethod(string value)a trie based approach is used.Recently I stumbled accross Fast parsing HTTP verbs from Wojciech Muła and gave it a try.
The approach uses gperf -- Perfect Hash Function Generator to generate a perfect hash function which computes / looks up a key which can be used to get the known HTTP-method.
Benchmark results
(I ran the benchmark several times to verify that the numbers are correct. I didn't believe in the first runs 😉 ... it's less code, traded with lookups)
Additional notes for gperf
As the lookup table isn't intuitive here the steps needed to create it.
Version of gperf: 3.1
File
knownverbs.txt:Command to run the generator:
This generator spits out C-code, which got ported to C# afterwards.
Note: there are plenty more tools for creating perfect-hashes, but this one is by far the easiest to use.
Question: gperf is GNU General Public License. Here only the output of that tool is used. To my understanding using this here doesn't violate GPL, but please confirm.
Generated C-code