Skip to content

Commit 19b9747

Browse files
committed
JavaScriptEventLoop: Read the clock less often with yieldInterval
1 parent b8369d9 commit 19b9747

2 files changed

Lines changed: 16 additions & 7 deletions

File tree

‎Sources/JavaScriptEventLoop/JavaScriptEventLoop+YieldInterval.swift‎

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,8 @@ extension JavaScriptEventLoop {
2020
return performanceNow!.now(this: performanceNow!.performance).number!
2121
}
2222

23-
func hasRun(since start: DrainInstant, for interval: Duration) -> Bool {
24-
let (seconds, attoseconds) = interval.components
25-
let intervalMilliseconds = Double(seconds) * 1_000 + Double(attoseconds) / 1_000_000_000_000_000
26-
return drainClockNow() - start >= intervalMilliseconds
23+
func drainElapsed(since start: DrainInstant) -> Duration {
24+
.milliseconds(drainClockNow() - start)
2725
}
2826
#else
2927
typealias DrainInstant = ContinuousClock.Instant
@@ -32,8 +30,8 @@ extension JavaScriptEventLoop {
3230
ContinuousClock.now
3331
}
3432

35-
func hasRun(since start: DrainInstant, for interval: Duration) -> Bool {
36-
start.duration(to: .now) >= interval
33+
func drainElapsed(since start: DrainInstant) -> Duration {
34+
start.duration(to: .now)
3735
}
3836
#endif
3937

‎Sources/JavaScriptEventLoop/JobQueue.swift‎

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ extension JavaScriptEventLoop {
4545

4646
// The clock is only read while `yieldInterval` is set.
4747
var start: DrainInstant? = yieldInterval == nil ? nil : drainClockNow()
48+
// A clock read costs more than a small job. Read it every `clockStride` jobs, and double
49+
// the stride (up to 256) while the drain is far from the interval.
50+
var clockStride = 1
51+
var jobsUntilClockRead = 1
4852
while let job = self.claimNextFromQueue() {
4953
#if compiler(>=5.9)
5054
job.runSynchronously(on: self.asUnownedSerialExecutor())
@@ -54,11 +58,18 @@ extension JavaScriptEventLoop {
5458

5559
if let yieldInterval, queueState.headJob != nil {
5660
if let start {
57-
if hasRun(since: start, for: yieldInterval) {
61+
jobsUntilClockRead -= 1
62+
if jobsUntilClockRead > 0 { continue }
63+
let elapsed = drainElapsed(since: start)
64+
if elapsed >= yieldInterval {
5865
// Keep `isSpinning` set so new jobs wait for the continuation.
5966
queueHostTask { self.runAllJobs() }
6067
return
6168
}
69+
if elapsed * 4 < yieldInterval {
70+
clockStride = min(clockStride * 2, 256)
71+
}
72+
jobsUntilClockRead = clockStride
6273
} else {
6374
start = drainClockNow()
6475
}

0 commit comments

Comments
 (0)