1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-21 15:04:11 -05:00

perf(ext/http): Reduce size of ResponseBytesInner (#24840)

I noticed
[`set_response_body`](ce42f82b5a/ext/http/service.rs (L439-L443))
was unexpectedly hot in profiles, with most of the time being spent in
`memmove`.

It turns out that `ResponseBytesInner` was _massive_ (5624 bytes), so
every time we moved a `ResponseBytesInner` (for instance in
`set_response_body`) we were doing a >5kb memmove, which adds up pretty
quickly.

This PR boxes the two larger variants (the compression streams),
shrinking `ResponseBytesInner` to a reasonable 48 bytes.

---
  Benchmarked with a simple hello world server:
```ts
// hello-server.ts
Deno.serve((_req) => {
  return new Response("Hello world");
});
// run with `deno run -A hello-server.ts`
// in separate terminal `wrk -d 10s http://127.0.0.1:8000`
```

Main:
```
Running 10s test @ http://127.0.0.1:8000/
  2 threads and 10 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    53.39us    9.53us   0.98ms   92.78%
    Req/Sec    86.57k     3.56k   91.58k    91.09%
  1739319 requests in 10.10s, 248.81MB read
Requests/sec: 172220.92
Transfer/sec:     24.64MB
```

This PR:
```
Running 10s test @ http://127.0.0.1:8000/
  2 threads and 10 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    45.44us    8.49us   0.91ms   90.04%
    Req/Sec   100.65k     2.26k  102.65k    96.53%
  2022296 requests in 10.10s, 289.29MB read
Requests/sec: 200226.20
Transfer/sec:     28.64MB
```

So a nice ~15% bump. (With response body compression, the gain is ~10%
for gzip and neutral for brotli)
This commit is contained in:
Nathan Whitaker 2024-08-01 17:30:26 -07:00 committed by GitHub
parent 5c54dc5840
commit 930ccf928a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 8 additions and 6 deletions

View file

@ -92,9 +92,9 @@ pub enum ResponseBytesInner {
/// An uncompressed stream. /// An uncompressed stream.
UncompressedStream(ResponseStream), UncompressedStream(ResponseStream),
/// A GZip stream. /// A GZip stream.
GZipStream(GZipResponseStream), GZipStream(Box<GZipResponseStream>),
/// A Brotli stream. /// A Brotli stream.
BrotliStream(BrotliResponseStream), BrotliStream(Box<BrotliResponseStream>),
} }
impl std::fmt::Debug for ResponseBytesInner { impl std::fmt::Debug for ResponseBytesInner {
@ -133,9 +133,11 @@ impl ResponseBytesInner {
fn from_stream(compression: Compression, stream: ResponseStream) -> Self { fn from_stream(compression: Compression, stream: ResponseStream) -> Self {
match compression { match compression {
Compression::GZip => Self::GZipStream(GZipResponseStream::new(stream)), Compression::GZip => {
Self::GZipStream(Box::new(GZipResponseStream::new(stream)))
}
Compression::Brotli => { Compression::Brotli => {
Self::BrotliStream(BrotliResponseStream::new(stream)) Self::BrotliStream(Box::new(BrotliResponseStream::new(stream)))
} }
_ => Self::UncompressedStream(stream), _ => Self::UncompressedStream(stream),
} }

View file

@ -545,10 +545,10 @@ impl Body for HttpRecordResponse {
ready!(Pin::new(stm).poll_frame(cx)) ready!(Pin::new(stm).poll_frame(cx))
} }
ResponseBytesInner::GZipStream(stm) => { ResponseBytesInner::GZipStream(stm) => {
ready!(Pin::new(stm).poll_frame(cx)) ready!(Pin::new(stm.as_mut()).poll_frame(cx))
} }
ResponseBytesInner::BrotliStream(stm) => { ResponseBytesInner::BrotliStream(stm) => {
ready!(Pin::new(stm).poll_frame(cx)) ready!(Pin::new(stm.as_mut()).poll_frame(cx))
} }
}; };
// This is where we retry the NoData response // This is where we retry the NoData response