Skip to content

Setup Basic Benchmarks for HTTP3 - #128

Open
nerdsupremacist wants to merge 8 commits into
swift-server:mainfrom
nerdsupremacist:mq-benchmarks
Open

nerdsupremacist wants to merge 8 commits into
swift-server:mainfrom
nerdsupremacist:mq-benchmarks

Conversation

@nerdsupremacist

Copy link
Copy Markdown
Collaborator

Motivation

We aim to work on improving performance of the HTTP Server. In order to do so effectively, it makes sense to have a set of benchmarks to measure how we improve over time.

Modifications

Adds a set of Benchmarks. Some are related to specific aspects of the flow. And Some are related to the general integration e2e of the server. We will track instructions, allocations, and time.

  1. How expensive is it to setup/boot up the server itself
  2. How expensive is it to open a new connection
  3. How expensive is it to start a new stream on an existing connection and download 1kb

In terms of more integrations, we can combine those 3 steps as follows:

  1. How expensive is it to setup a server, open a new connection, start a stream and download 1kb all together (most pessimistic scenario)
  2. How expensive is it to open a new connection, new stream and download 1kb on an existing pre warmed server (realistic for short lived requests)

Notes for reviewers:

  1. This is my first PR working with NIO this heavily. It is very possible that I'm doing something very wrong.
  2. I wanted to avoid benchmarking while using real sockets. So I had to make one function to be exposed over SPI in order to get the server running with a test channel. If this is not desired let me know. Or if we have a better way of doing this, I would appreciate some guidance
  3. I'm for now still on the fence if we should be tracking any of these benchmarks using thresholds and the CI, since HTTP Server does depend on a lot of other packages which mean that performance changes might be hard to isolate to only the code in this package. I tried my best to setup benchmarks so they mainly run the http server code, but of course improvements in QUIC, HTTP Types, etc will improve these benchmarks as well, which might make tracking using CI difficult. I don't know how possible it is, but if we could have CI only verify against regressions rather than be red over improvements elsewhere in the dependency stack, that might be a good compromise

Result

=======================
NIOHTTPServerBenchmarks
=======================

HTTP3_openConnection
╒══════════════════════════╤═══════════╤═══════════╤═══════════╤═══════════╤═══════════╤═══════════╤═══════════╤═══════════╕
│ Metric                   │        p0 │       p25 │       p50 │       p75 │       p90 │       p99 │      p100 │   Samples │
╞══════════════════════════╪═══════════╪═══════════╪═══════════╪═══════════╪═══════════╪═══════════╪═══════════╪═══════════╡
│ Instructions (M) *       │        23 │        24 │        24 │        24 │        24 │        25 │        28 │      1554 │
├──────────────────────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┤
│ Malloc (total) *         │      3554 │      4179 │      4191 │      4203 │      4215 │      4227 │      4252 │      1554 │
├──────────────────────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┤
│ Time (wall clock) (μs) * │      1201 │      1247 │      1261 │      1275 │      1290 │      1451 │      1886 │      1554 │
╘══════════════════════════╧═══════════╧═══════════╧═══════════╧═══════════╧═══════════╧═══════════╧═══════════╧═══════════╛

HTTP3_serverSetup
╒══════════════════════════╤═══════════╤═══════════╤═══════════╤═══════════╤═══════════╤═══════════╤═══════════╤═══════════╕
│ Metric                   │        p0 │       p25 │       p50 │       p75 │       p90 │       p99 │      p100 │   Samples │
╞══════════════════════════╪═══════════╪═══════════╪═══════════╪═══════════╪═══════════╪═══════════╪═══════════╪═══════════╡
│ Instructions (K) *       │      5489 │      5517 │      5530 │      5542 │      5554 │      5607 │      5983 │      6783 │
├──────────────────────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┤
│ Malloc (total) *         │       133 │       136 │       136 │       137 │       142 │       155 │       191 │      6783 │
├──────────────────────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┤
│ Time (wall clock) (μs) * │       248 │       260 │       268 │       275 │       286 │       306 │       402 │      6783 │
╘══════════════════════════╧═══════════╧═══════════╧═══════════╧═══════════╧═══════════╧═══════════╧═══════════╧═══════════╛

HTTP3_stream
╒══════════════════════════╤═══════════╤═══════════╤═══════════╤═══════════╤═══════════╤═══════════╤═══════════╤═══════════╕
│ Metric                   │        p0 │       p25 │       p50 │       p75 │       p90 │       p99 │      p100 │   Samples │
╞══════════════════════════╪═══════════╪═══════════╪═══════════╪═══════════╪═══════════╪═══════════╪═══════════╪═══════════╡
│ Instructions (K) *       │      3710 │      3854 │      3881 │      3916 │      3969 │      4178 │      4947 │      7191 │
├──────────────────────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┤
│ Malloc (total) *         │       888 │       900 │       903 │       903 │       905 │       959 │      1033 │      7191 │
├──────────────────────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┤
│ Time (wall clock) (μs) * │       221 │       251 │       256 │       263 │       270 │       295 │       920 │      7191 │
╘══════════════════════════╧═══════════╧═══════════╧═══════════╧═══════════╧═══════════╧═══════════╧═══════════╧═══════════╛

===============================
NIOHTTPServerEndToEndBenchmarks
===============================

HTTP3_openConnection_stream
╒══════════════════════════╤═══════════╤═══════════╤═══════════╤═══════════╤═══════════╤═══════════╤═══════════╤═══════════╕
│ Metric                   │        p0 │       p25 │       p50 │       p75 │       p90 │       p99 │      p100 │   Samples │
╞══════════════════════════╪═══════════╪═══════════╪═══════════╪═══════════╪═══════════╪═══════════╪═══════════╪═══════════╡
│ Instructions (M) *       │        28 │        29 │        29 │        29 │        29 │        30 │        32 │      1216 │
├──────────────────────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┤
│ Malloc (total) *         │      5159 │      5315 │      5319 │      5323 │      5343 │      5363 │      5409 │      1216 │
├──────────────────────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┤
│ Time (wall clock) (μs) * │      1515 │      1583 │      1604 │      1630 │      1679 │      1871 │      3642 │      1216 │
╘══════════════════════════╧═══════════╧═══════════╧═══════════╧═══════════╧═══════════╧═══════════╧═══════════╧═══════════╛

HTTP3_serverSetup_openConnection_stream
╒══════════════════════════╤═══════════╤═══════════╤═══════════╤═══════════╤═══════════╤═══════════╤═══════════╤═══════════╕
│ Metric                   │        p0 │       p25 │       p50 │       p75 │       p90 │       p99 │      p100 │   Samples │
╞══════════════════════════╪═══════════╪═══════════╪═══════════╪═══════════╪═══════════╪═══════════╪═══════════╪═══════════╡
│ Instructions (M) *       │        34 │        35 │        35 │        35 │        35 │        36 │        36 │      1010 │
├──────────────────────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┤
│ Malloc (total) *         │      5507 │      5539 │      5547 │      5555 │      5571 │      5603 │      5616 │      1010 │
├──────────────────────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┼───────────┤
│ Time (wall clock) (μs) * │      1843 │      1928 │      1947 │      1967 │      1997 │      2171 │      2835 │      1010 │
╘══════════════════════════╧═══════════╧═══════════╧═══════════╧═══════════╧═══════════╧═══════════╧═══════════╧═══════════╛

Comment on lines +15 to +16
import Benchmark
import Benchmark

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
import Benchmark
import Benchmark
import Benchmark

}.get()

let destination = ForwardingDestination(synchronizingOn: eventLoop)
let forwardTask = Task {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

We should avoid spawning unstructured tasks. This method should ideally be a with-style method that accepts a closure. In that closure, we would vend the channel as an argument. After the closure has finished executing (either normally or by throwing an error), we would handle cleanup by calling channel.close() for example.

TestHelpers in the test target has some examples of this pattern. See this for example.

The same also applies for BenchmarkHTTP3Server.

}
}

public final class BenchmarkHTTP3Client {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I see you've structured the package into multiple targets. public isn't necessary here, it should be replaced with package. The same applies to all other currently public types.


private func makeHTTP3BenchmarkConfiguration() -> Benchmark.Configuration {
.init(
metrics: [.mallocCountTotal, .instructions, .wallClock],

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We shouldn't add wallclock here since ideally we commit the thresholds and wall clock is unstable on CI/

)
}

let eventLoop = NIOAsyncTestingEventLoop()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm not sure how I feel with the artificial testing channel. I would rather us go through a loop back and send packets to it. The testing event loop makes this benchmark otherwise pretty different from what is actually happening.

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.

3 participants