From e92a05b5518e5fd30559c96c5990b08657bbc3e4 Mon Sep 17 00:00:00 2001 From: Nathan Whitaker <17734409+nathanwhit@users.noreply.github.com> Date: Wed, 14 Aug 2024 15:26:21 -0700 Subject: feat(serve): Opt-in parallelism for `deno serve` (#24920) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a `parallel` flag to `deno serve`. When present, we spawn multiple workers to parallelize serving requests. ```bash deno serve --parallel main.ts ``` Currently on linux we use `SO_REUSEPORT` and rely on the fact that the kernel will distribute connections in a round-robin manner. On mac and windows, we sort of emulate this by cloning the underlying file descriptor and passing a handle to each worker. The connections will not be guaranteed to be fairly distributed (and in practice almost certainly won't be), but the distribution is still spread enough to provide a significant performance increase. --- (Run on an Macbook Pro with an M3 Max, serving `deno.com` baseline:: ``` ❯ wrk -d 30s -c 125 --latency http://127.0.0.1:8000 Running 30s test @ http://127.0.0.1:8000 2 threads and 125 connections Thread Stats Avg Stdev Max +/- Stdev Latency 239.78ms 13.56ms 330.54ms 79.12% Req/Sec 258.58 35.56 360.00 70.64% Latency Distribution 50% 236.72ms 75% 248.46ms 90% 256.84ms 99% 268.23ms 15458 requests in 30.02s, 2.47GB read Requests/sec: 514.89 Transfer/sec: 84.33MB ``` this PR (`with --parallel` flag) ``` ❯ wrk -d 30s -c 125 --latency http://127.0.0.1:8000 Running 30s test @ http://127.0.0.1:8000 2 threads and 125 connections Thread Stats Avg Stdev Max +/- Stdev Latency 117.40ms 142.84ms 590.45ms 79.07% Req/Sec 1.33k 175.19 1.77k 69.00% Latency Distribution 50% 22.34ms 75% 223.67ms 90% 357.32ms 99% 460.50ms 79636 requests in 30.07s, 12.74GB read Requests/sec: 2647.96 Transfer/sec: 433.71MB ``` --- runtime/js/99_main.js | 49 +++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 41 insertions(+), 8 deletions(-) (limited to 'runtime/js') diff --git a/runtime/js/99_main.js b/runtime/js/99_main.js index ca96e34b7..5e25a3818 100644 --- a/runtime/js/99_main.js +++ b/runtime/js/99_main.js @@ -45,6 +45,7 @@ const { PromiseResolve, SafeSet, StringPrototypeIncludes, + StringPrototypePadEnd, StringPrototypeSplit, StringPrototypeTrim, Symbol, @@ -709,8 +710,37 @@ function bootstrapMainRuntime(runtimeOptions, warmup = false) { 11: mode, 12: servePort, 13: serveHost, + 14: serveIsMain, + 15: serveWorkerCount, } = runtimeOptions; + if (mode === executionModes.serve) { + if (serveIsMain && serveWorkerCount) { + const origLog = console.log; + const origError = console.error; + const prefix = `[serve-worker-0 ]`; + console.log = (...args) => { + return origLog(prefix, ...new primordials.SafeArrayIterator(args)); + }; + console.error = (...args) => { + return origError(prefix, ...new primordials.SafeArrayIterator(args)); + }; + } else if (serveWorkerCount !== null) { + const origLog = console.log; + const origError = console.error; + const base = `serve-worker-${serveWorkerCount + 1}`; + // 15 = "serve-worker-nn".length, assuming + // serveWorkerCount < 100 + const prefix = `[${StringPrototypePadEnd(base, 15, " ")}]`; + console.log = (...args) => { + return origLog(prefix, ...new primordials.SafeArrayIterator(args)); + }; + console.error = (...args) => { + return origError(prefix, ...new primordials.SafeArrayIterator(args)); + }; + } + } + if (mode === executionModes.run || mode === executionModes.serve) { let serve = undefined; core.addMainModuleHandler((main) => { @@ -725,13 +755,16 @@ function bootstrapMainRuntime(runtimeOptions, warmup = false) { } if (mode === executionModes.serve && !serve) { - console.error( - `%cerror: %cdeno serve requires %cexport default { fetch }%c in the main module, did you mean to run \"deno run\"?`, - "color: yellow;", - "color: inherit;", - "font-weight: bold;", - "font-weight: normal;", - ); + if (serveIsMain) { + // Only error if main worker + console.error( + `%cerror: %cdeno serve requires %cexport default { fetch }%c in the main module, did you mean to run \"deno run\"?`, + "color: yellow;", + "color: inherit;", + "font-weight: bold;", + "font-weight: normal;", + ); + } return; } @@ -746,7 +779,7 @@ function bootstrapMainRuntime(runtimeOptions, warmup = false) { ); } if (mode === executionModes.serve) { - serve({ servePort, serveHost }); + serve({ servePort, serveHost, serveIsMain, serveWorkerCount }); } } }); -- cgit v1.2.3