From d157157721942f4dfef3b148111b8be5c47bfeab Mon Sep 17 00:00:00 2001 From: Karl Seguin Date: Wed, 23 Sep 2026 20:02:47 +0800 Subject: [PATCH] perf: reduce delay in running v8 background jobs In Runner, when we're `in_cdp` we reduce the HttpClient poll time to 10ms when a v8 tells us it has a background task. This is to help ensure we don't linger too long in HttpClient's poll when v8 has work. But, 10ms can still be long, especially in a WPT test that's running thousands of WASM compilation in serial. So rather than having a flat 10ms wait, Runner will now wait 0-10ms, based on (a) whether the last pump had any tasks and (b) the number of _ticks since there was task. This is potentially a temporary solution to having v8 wake the HttpClient when a task is ready. --- src/browser/Browser.zig | 5 +++-- src/browser/Runner.zig | 14 ++++++++++++-- src/browser/js/Env.zig | 8 ++++++-- src/browser/js/Local.zig | 2 +- 4 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/browser/Browser.zig b/src/browser/Browser.zig index d69b604d6..5f1257efe 100644 --- a/src/browser/Browser.zig +++ b/src/browser/Browser.zig @@ -262,14 +262,15 @@ pub fn runMicrotasks(self: *Browser) void { self.env.runMicrotasks(); } -pub fn runMacrotasks(self: *Browser) !void { +pub fn runMacrotasks(self: *Browser) !bool { const env = &self.env; try self.env.runMacrotasks(); - env.pumpMessageLoop(); + const ran_platform_task = env.pumpMessageLoop(); // either of the above could have queued more microtasks env.runMicrotasks(); + return ran_platform_task; } pub fn hasBackgroundTasks(self: *Browser) bool { diff --git a/src/browser/Runner.zig b/src/browser/Runner.zig index 5264accb9..c2fb72857 100644 --- a/src/browser/Runner.zig +++ b/src/browser/Runner.zig @@ -35,6 +35,7 @@ const Runner = @This(); session: *Session, browser: *Browser, http_client: *HttpClient, +background_poll_ms: u32 = 0, pub const Opts = struct {}; @@ -223,8 +224,9 @@ fn _tick(self: *Runner, comptime is_cdp: bool, timeout_ms: u32, conditions: []Wa const has_runnable_page = hasRunnablePage(session); + var ran_platform_task = false; if (has_runnable_page) { - try browser.runMacrotasks(); + ran_platform_task = try browser.runMacrotasks(); } const activity = http_client.activity(); @@ -318,8 +320,16 @@ fn _tick(self: *Runner, comptime is_cdp: bool, timeout_ms: u32, conditions: []Wa break :blk 200; } if (browser.hasBackgroundTasks()) { + // if our last runMacrotasks() ran something and we now have + // a background, then don't linger in the http client waiting + // for I/O, instead, hurry back to run more tasks. + // Else, backoff to 10ms between runs. + // TODO: this is a temporary solution to ensuring background + // tasks are run promptly.The better solution is to have v8 + // wakeup the http client when there's work to do. + self.background_poll_ms = if (ran_platform_task) 0 else @min(10, @max(1, self.background_poll_ms * 2)); // msToNextTask could be less than this, but 10ms drift is ok - break :blk 10; + break :blk self.background_poll_ms; } break :blk browser.msToNextTask() orelse 200; }; diff --git a/src/browser/js/Env.zig b/src/browser/js/Env.zig index 40118c7fc..799ed3b9f 100644 --- a/src/browser/js/Env.zig +++ b/src/browser/js/Env.zig @@ -567,14 +567,18 @@ pub fn msToNextTask(self: *Env) ?u64 { return if (next_task == std.math.maxInt(u64)) null else next_task; } -pub fn pumpMessageLoop(self: *const Env) void { +pub fn pumpMessageLoop(self: *const Env) bool { var hs: v8.HandleScope = undefined; v8.v8__HandleScope__CONSTRUCT(&hs, self.isolate.handle); defer v8.v8__HandleScope__DESTRUCT(&hs); const isolate = self.isolate.handle; const platform = self.platform.handle; - while (v8.v8__Platform__PumpMessageLoop(platform, isolate, false)) {} + var ran = false; + while (v8.v8__Platform__PumpMessageLoop(platform, isolate, false)) { + ran = true; + } + return ran; } pub fn hasBackgroundTasks(self: *const Env) bool { diff --git a/src/browser/js/Local.zig b/src/browser/js/Local.zig index 1dc926183..f56bc9a99 100644 --- a/src/browser/js/Local.zig +++ b/src/browser/js/Local.zig @@ -121,7 +121,7 @@ pub fn newCallback( pub fn runMacrotasks(self: *const Local) void { const env = self.ctx.env; - env.pumpMessageLoop(); + _ = env.pumpMessageLoop(); env.runMicrotasks(); // macrotasks can cause microtasks to queue }