Skip to content

perf: consider moving away from HrTime #6553

@dyladan

Description

@dyladan

HrTime is the interface used to store timestamps in SpanImpl, metric Aggregators and Instruments, and LogRecords. It can also be used as the time input for starting and ending spans, metric times, and log times. We use it because in node, process.hrtime() has nanosecond precision which is better than the microsecond precision of performance.now(). process.hrtime() is no longer used. The only reference to process.hrtime in this repo is a comment in time.ts. Both Node and browser platforms use performance.now(). The nanosecond precision HrTime was designed for is never actually captured.

The use of HrTime as our internal time format has caused it to spread all over our code including into browser code where it was never meant to live. The worst example of this is in the fetch instrumentation which filters entries from the performance resource timing API by timestamp. In order to do that it creates timestamps with Date.now, converts them to HrTime, and calls @opentelemetry/sdk-trace-web#filterResourcesForSpan. That function converts them back to number nanoseconds before comparing them to the DOMHighResTimestamps used by the Performance Resource Timing API, including converting those to nanoseconds as well. HrTime is then used in the SpanImpl and passed to exporters where they are converted yet again to a nanosecond timestamp for export.

Proposal

  • Use millisecond number types for internal timestamps, which matches Date.now and performance.now. In 2026, the timeOrigin in milliseconds is roughly 1.76e12. In the IEEE 754 floating point format used by JS the mantissa is 53 bits. It takes around 41 bits to encode the integer part of timeOrigin, leaving 12 bits for the sub-millisecond part. This means the maximum resolution for a timestamp in millisecond number format is about 0.000244 milliseconds or 244 nanoseconds. Notably, this is well within the 5-100 microsecond jitter introduced by browsers to prevent timing attacks. This jitter is not introduced by node, but the performance.now() used by span start and end is only precise to 1 microsecond anyway, which is still 4x larger than 244 nanoseconds.
  • Deprecate HrTime as a time input format. We will have to keep it around as long as the API accepts it (which may be forever), but we can discourage its use and educate our users about its drawbacks.

Long term

  • We can consider if using something like Temporal is a good fit once it gains wider acceptance.
  • We can consider allowing custom clock implementations that can implement their own performance/precision tradeoffs. One thing to note is that a custom clock needs some interface with the exporters which is ideally lossless, but if we use BigInt that could nullify any performance benefit of using custom clocks. Maybe they need a toProtobuf and toStringNanosecond interface? A question for another time

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