Test origin is an nginx server that replies with an status 200 response and no body:
curl -v 127.0.0.1
* Trying 127.0.0.1:80...
* Connected to 127.0.0.1 (127.0.0.1) port 80 (#0)
> GET / HTTP/1.1
> Host: 127.0.0.1
> User-Agent: curl/7.72.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Server: nginx/1.18.0
< Date: Thu, 14 Jan 2021 17:00:25 GMT
< Content-Type: text/plain
< Content-Length: 0
< Connection: keep-alive
<
* Connection #0 to host 127.0.0.1 left intact
static_resources:
listeners:
- name: listener_0
address:
socket_address:
protocol: TCP
address: 0.0.0.0
port_value: 10000
filter_chains:
- filters:
- name: envoy.filters.network.http_connection_manager
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
stat_prefix: ingress_http
route_config:
name: local_route
virtual_hosts:
- name: fortio
domains: ["*"]
routes:
- match:
prefix: "/"
route:
cluster: fortio
http_filters:
- name: envoy.filters.http.router
clusters:
- name: fortio
connect_timeout: 1s
type: STRICT_DNS
# Comment out the following line to test on v6 networks
# dns_lookup_family: V4_ONLY
# lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: fortio
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: 127.0.0.1
port_value: 80
For closed loop testing:
taskset -c 0 bazel-bin/nighthawk_client --duration 60 --prefetch-connections --connections 100 -v info --rps 1000 http://127.0.0.1:10000
For open loop testing:
taskset -c 0 bazel-bin/nighthawk_client --open-loop --duration 60 --prefetch-connections --connections 100 -v info --rps 1000 http://127.0.0.1:10000
For adaptive load testing, the demo from envoyproxy/nighthawk#612 ...
Latency:
RPS | BASELINE http_parser | llhttp | Factory with http_parser |
---|---|---|---|
10k | 000ms 144us | 000ms 153us | 000ms 144us |
100k | 001ms 681us | 001ms 661us | 001ms 733us |
1M | 000ms 147us | 000ms 147us | 000ms 149us |
benchmark.http_2xx per second: (upstream_rq_total per second)
RPS | BASELINE http_parser | llhttp | Factory with http_parser |
---|---|---|---|
10k | 9999.98 | 9999.98 | 9999.98 |
100k | 19537.03 | 19771.98 | 18953.13 |
1M | 6430.72 | 6455.37 | 6374.03 |
Latency:
RPS | BASELINE http_parser | llhttp | Factory with http_parser |
---|---|---|---|
10k | 000ms 144us | 000ms 153us | 000ms 141us |
100k | 004ms 802us | 004ms 801us | 004ms 893us |
500k | 004ms 859us | 004ms 737us | 004ms 840us |
benchmark.http_2xx per second: (upstream_rq_total per second)
RPS | BASELINE http_parser | llhttp | Factory with http_parser |
---|---|---|---|
10k | 9996.36 | 9991.66 | 9999.18 |
100k | 20776.12 | 20780.71 | 20389.11 |
500k | 20531.76 | 21057.95 | 20611.05 |
An adaptive load test was run using Nighthawk’s adaptive controller (github.com/nighthawk/issues/416). The tool runs a nighthawk service and adjusts RPS based on feedback signals. The tool was configured to runs with an initial RPS of 10000, and feedback inputs:
- <5ms for mean latency (limiting factor)
- >95% success rate for received responses
- | BASELINE http_parser | llhttp | Factory with http_parser | Factory with llhttp |
---|---|---|---|---|
Final RPS | 20125 | 20156 | 19531 | 19843 |
The CPU profile was collected with gperftools
and pprof
. At 100k RPS (1 min), comparing the baseline with the llhttp swap in:
$ pprof -text -diff_base /tmp/http_parser.cpuprof /tmp/llhttp.cpuprof
File: envoy-static
Build ID: a4bf24672eeffa8fb0cfced8582945e4974b4d0c
Type: cpu
Showing nodes accounting for 0.12s, 0.2% of 59.97s total
Dropped 24 nodes (cum <= 0.30s)
flat flat% sum% cum cum%
-1.31s 2.18% 2.18% -32.34s 53.93% http_parser_execute
0.78s 1.30% 0.88% 32.17s 53.64% llhttp__internal__run
-0.25s 0.42% 1.30% -0.25s 0.42% epoll_ctl
0.22s 0.37% 0.93% 0.22s 0.37% __GI___writev (inline)
-0.21s 0.35% 1.28% -0.21s 0.35% __GI___readv (inline)
Comparing the baseline with the added factory:
pprof -text -focus=Parser -diff_base /tmp/http_parser.cpuprof /tmp/factory.cpuprof
File: envoy-static
Build ID: a4bf24672eeffa8fb0cfced8582945e4974b4d0c
Type: cpu
Active filters:
focus=Parser
Showing nodes accounting for 0.28s, 0.47% of 59.97s total
flat flat% sum% cum cum%
0.06s 0.1% 0.1% 0.06s 0.1% Envoy::Http::Http1::LegacyHttpParserImpl::getErrno
-0.05s 0.083% 0.017% -0.05s 0.083% Envoy::Router::HeaderParser::evaluateHeaders
0.04s 0.067% 0.083% 0.04s 0.067% Envoy::Http::Http1::LegacyHttpParserImpl::method
0.04s 0.067% 0.15% 0.07s 0.12% Envoy::Http::Http1::LegacyHttpParserImpl::pause
0.03s 0.05% 0.2% 0.03s 0.05% Envoy::Http::Http1::LegacyHttpParserImpl::statusCode
0.03s 0.05% 0.25% 0.03s 0.05% Envoy::Http::Http1::LegacyHttpParserImpl::usesTransferEncoding
0.03s 0.05% 0.3% 0.03s 0.05% http_parser_pause
0.02s 0.033% 0.33% 0.02s 0.033% Envoy::Http::Http1::LegacyHttpParserImpl::Impl::Impl(http_parser_type, void*)::{lambda(http_parser*)#1}::__invoke
0.02s 0.033% 0.37% 0.02s 0.033% Envoy::Http::Http1::LegacyHttpParserImpl::Impl::Impl(http_parser_type, void*)::{lambda(http_parser*, char const*, unsigned long)#2}::__invoke
0.01s 0.017% 0.38% 0.01s 0.017% Envoy::Http::Http1::LegacyHttpParserImpl::Impl::Impl(http_parser_type, void*)::{lambda(http_parser*, char const*, unsigned long)#3}::__invoke
0.01s 0.017% 0.4% 0.01s 0.017% Envoy::Http::Http1::LegacyHttpParserImpl::execute
0.01s 0.017% 0.42% 0.01s 0.017% Envoy::Http::Http1::LegacyHttpParserImpl::flags
0.01s 0.017% 0.43% 0.01s 0.017% Envoy::Http::Http1::LegacyHttpParserImpl::httpMajor
0.01s 0.017% 0.45% 0.01s 0.017% Envoy::Http::Http1::LegacyHttpParserImpl::methodName
0.01s 0.017% 0.47% 0.01s 0.017% Envoy::Http::Http1::LegacyHttpParserImpl::resume
0 0% 0.47% 0.28s 0.47% Envoy::Event::FileEventImpl::assignEvents(unsigned int, event_base*)::$_1::__invoke
0 0% 0.47% 0.20s 0.33% Envoy::Http::CodecClient::CodecReadFilter::onData
0 0% 0.47% 0.20s 0.33% Envoy::Http::CodecClient::onData