Repository: denoland/deno
Pull request: perf(ext/http): Reduce size of `ResponseBytesInner`

I noticed [`set_response_body`](https://github.com/nathanwhit/deno/blob/ce42f82b5a985e5f1482dff97a7268019a8e79ea/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)

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

filename: ext/http/service.rs
patch: @@ -545,10 +545,10 @@ impl Body for HttpRecordResponse {
           ready!(Pin::new(stm).poll_frame(cx))
         }
         ResponseBytesInner::GZipStream(stm) => {
-          ready!(Pin::new(stm).poll_frame(cx))
+          ready!(Pin::new(stm.as_mut()).poll_frame(cx))
         }
         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