From 930ccf928aee3ce5befc0a7494e0f9caaf0c8c63 Mon Sep 17 00:00:00 2001 From: Nathan Whitaker <17734409+nathanwhit@users.noreply.github.com> Date: Thu, 1 Aug 2024 17:30:26 -0700 Subject: perf(ext/http): Reduce size of `ResponseBytesInner` (#24840) 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) --- ext/http/response_body.rs | 10 ++++++---- ext/http/service.rs | 4 ++-- 2 files changed, 8 insertions(+), 6 deletions(-) (limited to 'ext/http') diff --git a/ext/http/response_body.rs b/ext/http/response_body.rs index 42080e8c9..bac43bf3c 100644 --- a/ext/http/response_body.rs +++ b/ext/http/response_body.rs @@ -92,9 +92,9 @@ pub enum ResponseBytesInner { /// An uncompressed stream. UncompressedStream(ResponseStream), /// A GZip stream. - GZipStream(GZipResponseStream), + GZipStream(Box), /// A Brotli stream. - BrotliStream(BrotliResponseStream), + BrotliStream(Box), } 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), } diff --git a/ext/http/service.rs b/ext/http/service.rs index f38fec4f4..787e9babf 100644 --- a/ext/http/service.rs +++ b/ext/http/service.rs @@ -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 -- cgit v1.2.3