perf: use Date.now() in enabled path (−8.8% on 200K-call benchmark)#1027
Open
alanzabihi wants to merge 1 commit intodebug-js:masterfrom
Open
perf: use Date.now() in enabled path (−8.8% on 200K-call benchmark)#1027alanzabihi wants to merge 1 commit intodebug-js:masterfrom
alanzabihi wants to merge 1 commit intodebug-js:masterfrom
Conversation
Replaces Number(new Date()) with Date.now() in the enabled debug() hot path. Both expressions produce the same epoch-ms value; Date.now() returns it without allocating a Date object.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation
Every enabled
debug()call insrc/common.jsbuilds a timestamp this way:That allocates a
Datejust to coerce it to a number.Date.now()returns the same value without the allocation. Both evaluate to(new Date()).valueOf().Change
One line in
src/common.js:No other file is touched. No new dependencies. Output is byte-identical; fingerprints at the bottom.
Compatibility
Date.now()is ES5 (2009). The repo pinsengines.node: ">=6.0", well under the floor.src/common.jsis also used bysrc/browser.js;Date.now()works in Chrome 5+, Firefox 3+, Safari 4+, and IE 9+.Measurements
Run on an Intel Xeon W-2295 (18c/36t @ 3.0 GHz), Ubuntu 24.04, Node v25.9.0. Server idle, one Node process per sample.
Bench 1: isolated microbenchmark
50M iterations in a tight loop, median of 5 rounds, no other debug machinery in the loop.
Number(new Date())Date.now()The operation itself runs 3.48× faster. About 92 ns saved per call.
Bench 2: in-repo enabled-path benchmark
The repo's own
.polyresearch/bench.js. A single run is 50K iterations × 4 debug-call variants = 200K enabled calls, 7 internal samples, internal median. Outer loop runs 10 times to smooth out inter-process noise; I report the median of those per-run medians.Setup:
Run (in each worktree):
PRIMARY_MS(200K enabled calls)GUARD_MS(2M disabled calls)Worst patched run across the 10 samples was 289.55 ms, still below the baseline median of 299.26 ms. No overlap in the middle of the distribution, so the gain is clear of run-to-run noise at n=10.
Caveats
%s/%d,%O, and multiline messages. Real apps hit those at different rates, so their share of the saving will differ.debug(). The observed 132 ns/call saving in Bench 2 is close to Bench 1's 92 ns/call prediction, with a small extra slice probably from V8 no longer needing to allocate.Gates
Fresh patched worktree:
npx mocha test.js test.node.js: 16/16 passing.npx xo src/common.js src/node.js src/index.js: no errors; pre-existing warnings count unchanged.upstream/master(SHA-256 over captured output across all four call types, reported by.polyresearch/bench.js):FINGERPRINT_A(enabled)1990ffa01cb11326FINGERPRINT_B(disabled)4ac7e68fb1172f4bRisks
None I can see. The two expressions differ only in whether a
Dategets allocated; the number they return is the same IEEE 754 double (current epoch ms, rounded to integer at call time).debug.diff,debug.prev, anddebug.currall see the same values.