Skip to content

node:perf_hooks: capture timeOrigin at process start, not first read #3013

@andrewtdiz

Description

@andrewtdiz

Summary

Node performance.timeOrigin is fixed near process start, before user code first reads it. Perry initializes TIME_ORIGIN_MS lazily on the first time_origin_ms() call, so code that waits before touching performance.timeOrigin would anchor the origin too late.

Node 25.9.0 behavior

const start = Date.now();
setTimeout(() => {
  const { performance } = require("node:perf_hooks");
  console.log(Date.now() - start);              // about 80ms in this probe
  console.log(Date.now() - performance.timeOrigin); // already includes startup + wait time
  console.log(performance.now());               // similar process-relative elapsed time
}, 80);

Local Node v25.9.0 reported about 83ms elapsed before the first read, while Date.now() - performance.timeOrigin and performance.now() were already about 117ms. The origin was not captured lazily at the first performance access.

Perry source evidence

  • crates/perry-runtime/src/perf_hooks.rs defines static TIME_ORIGIN_MS: OnceLock<f64>.
  • time_origin_ms() calls TIME_ORIGIN_MS.get_or_init(|| SystemTime::now().duration_since(UNIX_EPOCH)...), so the origin is the wall-clock time of the first time_origin_ms() call.
  • performance.timeOrigin, performance.toJSON(), performance.nodeTiming, and event loop utilization all read this lazy value.

Expected

performance.timeOrigin should be initialized from a process-start timestamp, not from the first property/method access. This should remain true even if node:perf_hooks is imported or globalThis.performance is accessed later in the program.

Duplicate checks

  • performance.timeOrigin process start lazy
  • perf_hooks timeOrigin first read

I did not find an existing issue for lazy timeOrigin initialization. Related #3012 covers performance.now() being epoch-based, not the origin capture point itself.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions