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

Improve HTTP parsing long look-ahead #11486

Merged
merged 8 commits into from
Mar 5, 2024

Conversation

gregw
Copy link
Contributor

@gregw gregw commented Mar 3, 2024

Use ByteBuffer.getLong to look for entire request (GET / HTTP/1.1) or response (HTTP/1.1 200 OK) line with 2 long lookups. Failing that, a single long lookup is sufficient to determine the common methods and/or HttpVersion.

Use ByteBuffer.getLong to look for entire request (GET / HTTP/1.1) or response (HTTP/1.1 200 OK) line with 2 long lookups.  Failing that, a single long lookup is sufficient to determine the common methods and/or HttpVersion.
Copy link
Contributor

@lorban lorban left a comment

Choose a reason for hiding this comment

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

Since this is a perf improvement, a JMH benchmark to prove this change is worthy would be nice.

@gregw
Copy link
Contributor Author

gregw commented Mar 4, 2024

@lorban done.

Note we already have a JMH for HttpMethod, showing that an int lookup is much better than a byte by byte walk of a Trie.
The new JMH has a longLookAhead test that compares a double long look-ahead for a whole "GET / HTTP/1.1\r\n" line. It contrasts that with a parse that does:

  • HttpMethod.lookAheadGet which already does the getInt optimization
  • a loop to scan for a space and builds the uri String
  • a HttpVersion.CACHE.getBest which does a byte by byte walk through a Trie.

The tests are give a GET request (which will match the longs) or a POST request (which will not). If the long lookAhead misses, it calls the normal parse.
We randomly select GET/POST and test at 100%, 10%, 1% and 0% GETs.

JMH results are:

Benchmark                         (hits)   Mode  Cnt          Score         Error  Units
HttpParseBenchmark.testLookAhead     100  thrpt   10  240379914.665 ± 3429730.973  ops/s
HttpParseBenchmark.testLookAhead      10  thrpt   10   23525864.889 ±  445015.567  ops/s
HttpParseBenchmark.testLookAhead       1  thrpt   10   22566491.351 ±  185088.951  ops/s
HttpParseBenchmark.testLookAhead       0  thrpt   10   20077312.903 ± 2444892.471  ops/s
HttpParseBenchmark.testParse         100  thrpt   10   20093713.476 ±  330022.786  ops/s
HttpParseBenchmark.testParse          10  thrpt   10   19120639.490 ±  349302.848  ops/s
HttpParseBenchmark.testParse           1  thrpt   10   19445451.997 ±  588423.954  ops/s
HttpParseBenchmark.testParse           0  thrpt   10   20429478.588 ±  518320.495  ops/s

For 100% hits, the performance of the long lookAhead is more than an order of magnitude better! 240M op/s vs 20M op/s ! So this is obviously a very advantageous technique if we can hit frequently. (@sbordet wow!)

For 0% hits, the lookAhead is slightly slower: 20.0M op/s vs 20.4M op/s. This is is within the error margins of the runs (+/- 2M op/s). So we can say that the cost of doing the lookAhead is insignificant even if it never hits.

For just 1% of hits, we get a small gain of 22.6M op/s vs 19.4 op/s, which is just above the margin of error (+/- 0.5M op/s)

For 10% hits the gain is a bit more 23.5M op/s vs 19.1 op/s, so it looks like gains are real.

Thus I think this will be really beneficial for the client as "HTTP/1.1 200 OK" should be a very frequent match.
On the server, GET / may be frequent for some apps and never happen for others. But as the cost is small and the gain can be modest, it looks like a good change.

lorban
lorban previously approved these changes Mar 4, 2024
Copy link
Contributor

@lorban lorban left a comment

Choose a reason for hiding this comment

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

Splendid! LGTM.

Added a fallback int lookup
@gregw
Copy link
Contributor Author

gregw commented Mar 4, 2024

@lorban I've just done a little tweak, so if the long looked up to try to match "GET / HT" misses, then the high int of that long is used by the HttpMethod.lookAheadGet method to look for "GET ", "PUT ", "POST" etc. So if we miss, this avoids the need to call getInt from the buffer. So can you check that code too and re-review.

gregw added 2 commits March 5, 2024 09:12
Added a fallback int lookup
sbordet
sbordet previously approved these changes Mar 5, 2024
Copy link
Contributor

@sbordet sbordet left a comment

Choose a reason for hiding this comment

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

Just a nit if you want to fix it. LGTM.

@gregw gregw dismissed stale reviews from lorban and sbordet via 3bd8038 March 5, 2024 14:23
@gregw
Copy link
Contributor Author

gregw commented Mar 5, 2024

@sbordet @lorban nits fixed. Re-review please

@gregw gregw merged commit 80f912a into jetty-12.0.x Mar 5, 2024
8 checks passed
@gregw gregw deleted the experiment/jetty-12.0.x/longLookups branch March 5, 2024 16:34
lorban added a commit that referenced this pull request Mar 13, 2024
lorban added a commit that referenced this pull request Mar 13, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
No open projects
Status: ✅ Done
Development

Successfully merging this pull request may close these issues.

3 participants