Skip to content

Construct new Date() from clock ticks without intermediate conversions#2609

Merged
lahma merged 2 commits into
sebastienros:mainfrom
lahma:perf-date-construction
Jul 9, 2026
Merged

Construct new Date() from clock ticks without intermediate conversions#2609
lahma merged 2 commits into
sebastienros:mainfrom
lahma:perf-date-construction

Conversation

@lahma

@lahma lahma commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator

What

The zero-argument new Date() path computed (GetUtcNow().DateTime - Epoch).TotalMilliseconds — a
DateTimeOffset → DateTime → TimeSpan → double chain — and then re-validated the value through
TimeClip inside the JsDate constructor. The milliseconds now come straight from ticks
((UtcTicks − EpochTicks) / TicksPerMillisecond), and a clip-free internal constructor stores them
directly: integer division truncates toward zero exactly like TimeClip for positive values, and a
ticks-derived current time is always inside the clip range. The subclass path
(OrdinaryCreateFromConstructor) keeps the spec pipeline.

Results

DateConstructionBenchmarks A/B (default job, adjacent runs):

row before after
NewDateNow 9.060 ms 8.048 ms −11.2% (90.6 → 80.5 ns per new Date())
NewDateMillis (guard, untouched path) 11.66 ms 11.81 ms flat — launchCount-5 median; a single-launch +7.6% was a process mode

Allocations byte-identical. Honest framing for stopwatch.js (which spends ~13% of its time in
new Date(), ~196k per op): the expected ~1% row gain sits inside the row's noise floor — the
±0.7% wall-clock driver read flat — so this is a micro-path win, not a row mover.

Gating

All-TFM build ✅ · Jint.Tests ×2 ✅ · PublicInterface ×2 ✅ · Test262 99,426/0 ✅

🤖 Generated with Claude Code

The zero-argument Date construction path computed
(GetUtcNow().DateTime - Epoch).TotalMilliseconds - a DateTimeOffset ->
DateTime -> TimeSpan -> double chain - and then re-validated the value
through TimeClip inside the JsDate constructor. The milliseconds now
come straight from ticks ((UtcTicks - EpochTicks) / TicksPerMillisecond),
and a clip-free internal constructor stores them directly: integer
division truncates toward zero exactly like TimeClip for positive
values, and a ticks-derived current time is always inside the clip
range. The subclass path (OrdinaryCreateFromConstructor) keeps the
spec pipeline.

DateConstructionBenchmarks A/B (default job, adjacent runs):

| row           | before   | after    |                          |
|---------------|----------|----------|--------------------------|
| NewDateNow    | 9.060 ms | 8.048 ms | -11.2% (90.6 -> 80.5 ns) |
| NewDateMillis | 11.66 ms | 11.81 ms | flat (launchCount-5 median; single-launch +7.6% was a process mode) |

Allocations byte-identical. The stopwatch row's expected ~1% gain sits
inside its noise floor (driver read flat) - this is a micro-path win on
the ~13% of that row spent constructing dates.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@lahma
lahma enabled auto-merge (squash) July 9, 2026 08:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant