Skip to content

node:perf_hooks: make performance.now relative to timeOrigin #3012

Closed

Description

@andrewtdiz

Summary

Node performance.now() returns high-resolution milliseconds since performance.timeOrigin, not milliseconds since the Unix epoch. Perry currently implements js_performance_now() as epoch milliseconds, which makes performance.now() roughly equal to Date.now() and breaks User Timing start times/durations that rely on the same helper.

Node 25.9.0 behavior

const { performance } = require("node:perf_hooks");

console.log(performance.now() < performance.timeOrigin); // true

const approxNow = performance.timeOrigin + performance.now();
console.log(Math.abs(approxNow - Date.now()) < 100);      // true

Local Node v25.9.0 reported performance.now() around tens of milliseconds, performance.timeOrigin around epoch milliseconds, and timeOrigin + now within 1ms of Date.now().

Perry source evidence

  • crates/perry-runtime/src/date.rs::js_performance_now() returns SystemTime::now().duration_since(UNIX_EPOCH).as_secs_f64() * 1000.0.
  • crates/perry-runtime/src/perf_hooks.rs::time_origin_ms() also stores an epoch-millisecond timestamp.
  • crates/perry-runtime/src/perf_hooks.rs::perf_now() delegates to crate::date::js_performance_now(), so default performance.mark() start times, performance.measure() durations, and event loop utilization calculations inherit epoch-millisecond values instead of relative timestamps.
  • Dynamic dispatch and static lowering for performance.now call js_performance_now() directly.

Expected

performance.now() should return a monotonic, high-resolution duration relative to the fixed performance.timeOrigin. performance.timeOrigin + performance.now() should approximate wall-clock Date.now(), while performance.now() itself should be a small process-relative value.

Duplicate checks

  • performance.now timeOrigin epoch
  • perf_hooks now relative timeOrigin
  • PR search for performance.now timeOrigin

I did not find an existing issue for performance.now() being epoch-based.

Activity

  1. andrewtdiz commented on May 31, 2026

    @andrewtdiz
    ContributorAuthor

    I am taking this in a focused node:perf_hooks timing and validation PR cut with #3013, #3011, #3010, #3008, and #3088. Intended scope: make performance.now() relative to a process-start timeOrigin, support two-argument eventLoopUtilization() diffs, and align the related User Timing / observer validation behavior. Non-goals: timerify() function entries and markResourceTiming() resource entries, which fit a separate observable-entry cut.

  2. andrewtdiz commented on May 31, 2026

    @andrewtdiz
    ContributorAuthor

    Opened PR #3508 to close this: #3508

    Verification on the rebased branch:

    • Node 24 parity: ./run_parity_tests.sh --suite node-suite --module perf_hooks passed 78/78.
    • cargo check -p perry-runtime -p perry-codegen -p perry-hir -p perry-api-manifest -p perry passed.
    • cargo fmt --all -- --check, ./scripts/check_file_size.sh, jq empty test-parity/known_failures.json, and git diff HEAD~1..HEAD --check passed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions