-
Notifications
You must be signed in to change notification settings - Fork 7.3k
http: fix >90% performance regression for GET requests using response.end #9026
Conversation
A significant performance regressions has been introduced in 1fddc1f for GET requests which send data through response.end(). The number of requests per second dropped to somewhere around 6% of their previous level. (#8940) The fix consists of removing a part of the lines added by 1fddc1f, lines which were supposed to affect only HEAD requests, but interfered with GET requests instead. The lines removed would not have affected the behaviour in the case of a HEAD request as this._hasBody would always be false. Therefore, they were not required to fix the issue reported in #8361.
+1. This confused me for a long time as to why our regular benchmarking in our pipeline dropped precipitously after 0.10.32. |
+100 |
I believe the correct fix is more likely
In terms of request/second, my results are even worse (on RHEL 7.0). There seems to be a fixed 40ms delay when it goes through the flush routine. Build 6e689ec
Build 1fddc1f
Build 1fddc1f with following patch applied:
|
When replying to a HEAD request, do not attempt to send the trailers and EOF sequence (`0\r\n\r\n`). The HEAD request MUST not have body. Quote from RFC: The presence of a message body in a response depends on both the request method to which it is responding and the response status code (Section 3.1.2). Responses to the HEAD request method (Section 4.3.2 of [RFC7231]) never include a message body because the associated response header fields (e.g., Transfer-Encoding, Content-Length, etc.), if present, indicate only what their values would have been if the request method had been GET (Section 4.3.1 of [RFC7231]). fix #8361 Reviewed-By: Timothy J Fontaine <tjfontaine@gmail.com>
@evantorrie Yeah, that's the change I had suggested in #8940 but I wasn't sure if it was 100% correct or not. |
For completeness, here's the source of the server that the
|
I'm fine with this. |
+1 as long as the tests pass |
@trevnorris @cjihrig What change do you agree with, the one mentioned by @evantorrie in his comment or the one from @CGavrila's PR? |
@joyent/node-coreteam Added to the 0.10.37 milestone as it seems to be a significant performance issue for which we seem to have a good candidate fix. |
I was originally referring to the changes in this PR. If @evantorrie's changes also work, but are faster, then I'd prefer that. |
It has been a while, but what I remember from when Cristian and I worked on this, is that we identified that this._hasBody is false for a HEAD and true for a GET. At the same time we also identified that if this._hasBody is false then hot is already false so the lines are unnecessary. So we believe that removing the lines only affects the case that needs to be fixed and felt that the fewer the lines the better. |
@misterdjules I was also referring to the change in this PR. |
Thank you for the clarifications! LGTM, running http benchmarks currently and tests on all platforms to make sure there's no regression. I will report results here. |
Benchmarks results available and no regression found on UNICES and on Windows. |
@trevnorris I'm not sure how to interpret these benchmark results, are you familiar with them? |
@misterdjules yeah. those results are a pain to read though, and overall I don't find them super reliable. I've tested it myself and it seems to help. |
@trevnorris Sounds good, I just wanted to make sure we didn't find anything suspicious in these benchmarks. Do you mind landing this? Thank you @CGavrila, @evantorrie and everyone who helped! |
Thank you, @misterdjules and the team, for the work done to validate this! |
A significant performance regressions has been introduced in 1fddc1f for GET requests which send data through response.end(). The number of requests per second dropped to somewhere around 6% of their previous level. The fix consists of removing a part of the lines added by 1fddc1f, lines which were supposed to affect only HEAD requests, but interfered with GET requests instead. The lines removed would not have affected the behaviour in the case of a HEAD request as this._hasBody would always be false. Therefore, they were not required to fix the issue reported in nodejs#8361. Fixes nodejs#8940. PR: nodejs#9026 PR-URL: nodejs#9026 Reviewed-By: Julien Gilli <julien.gilli@joyent.com>
Thank you everyone, landed in 8bcd0a4! |
Problem
This is an attempt to fix issue #8940, which describes a performance regression which started with commit 1fddc1f; more specifically, the number of requests per second dropped from ~18k to ~1.1k (on my machine) in between v0.10.31 and v0.10.32. This can be tested easily as described in issue #8940:
The problem comes from commit 1fddc1f, in particular from the following lines, which are aimed at HEAD requests:
However, the
hot = false;
instruction will never execute for HEAD requests, sincethis._hasBody
is alwaysfalse
because of line lib/http.js:1073, but it will execute for all GET requests since the conditions will match. Therefore, hot-path optimization will never be done for GET requests when sending data viaresponse.end()
.Fix
The fix is simply to remove the lines mentioned above:
As previously explained, this executes only for GET requests, not for HEAD requests, as it was intended. Simply removing them will allow GET requests to have the hot-path optimization when
response.end(...);
is not empty, while HEAD requests will be non-optimized since there is no need (i.e no body).Testing
As far as I can tell, there are three conditions which need to be met in order for the fix to be considered safe:
For no. 1, I have tested that the performance is at pre-1fddc1 levels after deleting those three lines.
For no. 2, I have made sure that the Node tests run/pass in the same way with/without the fix.
For no. 3, I have tried those scripts, everything is fine.