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

perf(fetch): optimize normalizeMethod() #10154

Merged
merged 4 commits into from
Apr 13, 2021

Conversation

AaronO
Copy link
Contributor

@AaronO AaronO commented Apr 12, 2021

Which was unnecessarily wasteful and taking up 5% of the JS CPU time in the deno_http_native bench.

It's not clear why byteUpperCase() was useful, especially since we only return the uppercased method if it matches a whitelist of unambiguous strings.

Which was unnecessarily wasteful and taking up 5% of the JS CPU time in the deno_http_native bench
@lucacasonato
Copy link
Member

See spec: https://fetch.spec.whatwg.org/#methods

To normalize a method, if it is a byte-case-insensitive match for DELETE, GET, HEAD, OPTIONS, POST, or PUT, byte-uppercase it.

Copy link
Member

@lucacasonato lucacasonato left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

toUpperCase is not the same as byte uppercase in whatwg infra:

"byte uppercase": https://infra.spec.whatwg.org/#byte-uppercase
"toUpperCase": https://tc39.es/ecma262/#sec-string.prototype.touppercase

One is ASCII only, the other uses the "Unicode Default Case Conversion"

@AaronO
Copy link
Contributor Author

AaronO commented Apr 12, 2021

toUpperCase is not the same as byte uppercase in whatwg infra:

"byte uppercase": https://infra.spec.whatwg.org/#byte-uppercase
"toUpperCase": https://tc39.es/ecma262/#sec-string.prototype.touppercase

One is ASCII only, the other uses the "Unicode Default Case Conversion"

I get that, but I don't see how the distinction is meaningful when we only return the upper cased version if it matches a fixed whitelist with strict string equality, I can't see how this could fail or have edge cases.

@lucacasonato
Copy link
Member

It might be the case that some unicode characters "upper case" is an ASCII char. For example that é uppercases to E (not the case, but you get my point). If you are sure this is not the case currently, and will never be the case in the future, it LGTM.

@AaronO
Copy link
Contributor Author

AaronO commented Apr 12, 2021

It might be the case that some unicode characters "upper case" is an ASCII char. For example that é uppercases to E (not the case, but you get my point). If you are sure this is not the case currently, and will never be the case in the future, it LGTM.

The only downside then would be a "false positive" normalization of an ambiguous user-provided method name. It's definitely not best-practice for users to use custom unicode method names ... so I don't think it's a huge issue or spec breaking to normalize in those situations.

Even if we want to preserve the previous behaviour, it still makes sense to add the isKnownMethod() first since that check should be true 99% of the time and is substantially faster than the spec's byte upper casing.

Copy link
Member

@ry ry left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's just move this to rust

@lucacasonato
Copy link
Member

lucacasonato commented Apr 12, 2021

Even if we want to preserve the previous behaviour, it still makes sense to add the isKnownMethod() first since that check should be true 99% of the time and is substantially faster than the spec's byte upper casing.

Totally. In isolation that change is great, and I think we should land it.

I don't think it's a huge issue or spec breaking to normalize in those situations

This is a very slippery slope. Having inconsistencies like these are something that some dev somewhere is going to spend hours debugging, because some edge case works (or doesn't work) in Chome, FF, and Safari, but not in Deno.

"Correctness is more important than perf" where your words, not mine ;-) https://discord.com/channels/684898665143206084/778060818046386176/830035065669025822

@AaronO
Copy link
Contributor Author

AaronO commented Apr 12, 2021

This is a very slippery slope. Having inconsistencies like these are something that some dev somewhere is going to spend hours debugging, because some edge case works (or doesn't work) in Chome, FF, and Safari, but not in Deno.

"Correctness is more important than perf" where your words, not mine ;-) https://discord.com/channels/684898665143206084/778060818046386176/830035065669025822

I completely agree, it's better to waste machine time than hours of a person's life :)

However the tests seem to pass, shouldn't the spec / edge-cases be enforced by the WPT tests ?

@lucacasonato
Copy link
Member

WPT is not yet enabled for fetch. I am not sure if this edge case has a WPT. If not, I'll write one and upstream it once we enable fetch WPT.

@ry
Copy link
Member

ry commented Apr 12, 2021

I'm very much in favor of removing this complexity all together. If someone is using non-upper case methods they deserve the bugs.

Please see #10155

@AaronO
Copy link
Contributor Author

AaronO commented Apr 12, 2021

I'm very much in favor of removing this complexity all together. If someone is using non-upper case methods they deserve the bugs.

Please see #10155

Pragmatically I wouldn't be opposed to it. The main thing I care about here is that we have a fast path and aren't wasting JS cpu time were we shouldn't be

@lucacasonato
Copy link
Member

I'm very much in favor of removing this complexity all together. If someone is using non-upper case methods they deserve the bugs.

Please see #10155

This is even more spec incompliant. Someone ran into this a few days ago in fetch, and it needed to be hardened even more: #10090. I am strongly opposed to removing normalization that the spec mandates. These are the kind of inconsistencies I have been trying to slowly weed out of our implementations for the last few months. As I have mentioned previously on multiple occasions, I am going to spend the next few days and weeks making fetch spec compliant and enabling WPT. Part of that will be rewriting these APIs. I will rewrite them in such a way that we can fast path skip the entire constructor for internal operations in the HTTP server. In light of that I would be opposed to "optimizing" this any more than the current state of this PR.

Copy link
Member

@lucacasonato lucacasonato left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Thanks for the changes.

@lucacasonato lucacasonato merged commit 9f26e63 into denoland:main Apr 13, 2021
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.

None yet

3 participants