Join GitHub today
GitHub is home to over 50 million developers working together to host and review code, manage projects, and build software together.Sign up
Improve WebFlux performance for header management [SPR-17250] #21783
In a recent reactor netty issue, small apps are being used to benchmark different frameworks. While working on that, Violeta Georgieva found a few interesting points we could take a look at, as opportunities for performance improvements and micro-benchmark cases.
The benchmark has underlined a few hostpots in the Spring Framework codebase:
Request Content-Type parsing
In general, we're spending a lot of time on
We could use a more optimized data structure behind
Benchmarks show that even a
Other implementations tend to wrap the
For Spring WebFlux, we could even wrap the native HTTP request headers and never copy those values in other data structures. This requires dedicated implementations for each server (Tomcat, Jetty, Reactor Netty and Undertow), but since most of those are using pooled resources for headers, we might improve a lot the GC pressure applied for each HTTP exchange handling.
Here are microbenchmark results on
The baseline is just performing those read operations from an existing map. the other variants are doing what Spring is supposed to do, treating that map as the native request headers. In the regular
This shows that the copying/allocations are using resources and changing the underlying implementation only slightly improves performance. This means we should instead look into leveraging the native headers directly and avoid copying in the first place.
Benchmark Mode Cnt Score Error Units 1. GET requests MyBenchmark.baselineGetRequest thrpt 5 890408.644 ± 25882.489 ops/s 1. current HttpHeaders implementation MyBenchmark.httpHeadersGetRequest thrpt 5 632354.108 ± 9054.069 ops/s 1. current implementation, but with a shortcut for linked map lookups MyBenchmark.fixedCaseInsensitiveHeadersGetRequest thrpt 5 671559.477 ± 22871.242 ops/s 1. same thing, but backed by a case insensitive TreeMap MyBenchmark.treeMapHttpHeadersGetRequest thrpt 5 734720.620 ± 17370.468 ops/s 1. same thing, but backed by a dedicated map implementation MyBenchmark.headersMapHttpHeadersGetRequest thrpt 5 695399.245 ± 24594.272 ops/s 1. POST requests 1. baseline benchmark MyBenchmark.baselinePostRequest thrpt 5 8672650.691 ± 135878.532 ops/s 1. current HttpHeaders implementation MyBenchmark.httpHeadersPostRequest thrpt 5 994670.287 ± 19372.729 ops/s 1. current implementation, but with a shortcut for linked map lookups MyBenchmark.fixedCaseInsensitiveHttpHeadersPostRequest thrpt 5 1058808.241 ± 131617.683 ops/s 1. same thing, but backed by a case insensitive TreeMap MyBenchmark.treeMapHttpHeadersPostRequest thrpt 5 1525237.824 ± 27530.672 ops/s 1. current implementation, but caching the content type once resolved MyBenchmark.httpHeadersContentTypeCachePostRequest thrpt 5 1584504.796 ± 65863.946 ops/s 1. caching the content type + backed by a dedicated map implementation MyBenchmark.optimizedHttpHeadersPostRequest thrpt 5 2310686.814 ± 93787.128 ops/s
Affects: 5.1 RC2
Reference URL: reactor/reactor-netty#392
0 votes, 7 watchers
Brian Clozel commented
After the analysis done in the issue description, we've decided to optimize on both fronts:
The first optimization directly shows improvements for POST/PUT requests where the server reads the request
The second optimization brings the most benefit for Spring WebFlux applications. All
When looking at hot methods with a high overhead profiler (sorted by their "own time", adding the total amount of time spent in them overall), we get the following result: amongst the top 3 hottest methods (all
We'll work on further optimizations in separate issues.
As of gh-21783, Spring WebFlux uses a `TomcatHeadersAdapter` implementation to directly address the native headers used by the server. In the case of Tomcat, "Content-Length" and "Content-Type" headers are processed separately and should not be added to the native headers map. This commit improves the `HandlerAdapter` implementation for Tomcat and removes those headers, if previously set in the map. The adapter already has a section that handles the Tomcat-specific calls for such headers. Fixes gh-24361
As of gh-21783, Spring WebFlux uses a `TomcatHeadersAdapter` implementation to directly address the native headers used by the server. In the case of Tomcat, "Content-Length" and "Content-Type" headers are processed separately and should not be added to the native headers map. This commit improves the `HandlerAdapter` implementation for Tomcat and removes those headers, if previously set in the map. The adapter already has a section that handles the Tomcat-specific calls for such headers. Fixes gh-24387