perf(quickjs): cache Date timezone offset lookups - #1687
Conversation
The timezone offset for a Date is recomputed on every lookup via
GetTimeZoneInformation() (Windows) or localtime_r() (Unix), both of
which are slow system calls. Cache it per runtime in a small ring
buffer keyed by a TZ hash with a 1s TTL, collapsing stable hours and
falling back to an exact second across DST boundaries.
Thread the JSRuntime through getTimezoneOffset and set_date_fields so
the cache is reachable. getenv("TZ") is read only on cache miss since
it can be surprisingly slow on some platforms; a TZ change is picked
up at most 1s later, matching the cache TTL.
Adds a focused regression test in tests/date-timezone-cache.js.
Measured on Windows x64 (MSVC): getTimezoneOffset 563 -> 126 ns
(-76%), Date.prototype.toString 1131 -> 613 ns (-46%).
|
What kind of use case do you have where this is somehow a problem? |
|
Hi @saghul, thanks for the question. The motivation actually came from the project's own benchmark: upstream Where this shows up in real code: loggers stamping every line with a local timestamp, telemetry/metrics exporters stamping every sample, dashboards/feeds rendering one timestamp per row — all calling Measured results: Windows x64 (MSVC):
macOS (Apple M2, release build, median of 3 runs):
The change is small and self-contained (~100 lines + one regression test) and keeps the raw path as fallback. Per the description: if the up-to-1s delay on |
9add32e to
dd57d02
Compare
|
My fear is that this may cause problems in long running processes when DST changes for example. @bnoordhuis thoughts on this? |
Summary
Date.prototype.getTimezoneOffset()(and anything that formats a local date string) recomputes the timezone offset on every call viaGetTimeZoneInformation()on Windows orlocaltime_r()on Unix. Both are slow system calls. This PR caches the offset per runtime.Changes
JSTimezoneOffsetCache, 8 entries) keyed by aTZhash with a 1s TTL. Stable UTC hours collapse into one cached range; across DST boundaries it falls back to caching the exact second.JSRuntimethroughgetTimezoneOffsetandset_date_fieldsso the cache is reachable.getenv("TZ")only on cache miss, not on every lookup: on some platformsgetenvis surprisingly slow (measured ~126 us here). ATZchange is therefore picked up at most 1s later, matching the cache TTL.tests/date-timezone-cache.jscovering repeated lookups, local-string round trips, Date limit values, andTZchanges (skipped on Windows, whereGetTimeZoneInformationreads the registry and does not honorTZ).Results
Measured on Windows x64 (MSVC):
getTimezoneOffset()Date.prototype.toString()Measured on macOS (Apple M2, CMake Release build, median of 3 runs):
getTimezoneOffset()Date.prototype.toString()Date.parse()(local string)date_parsemicrobench (bellard/quickjs)Notes
make test: 0/111 errors (Windows x64 and macOS).getenv("TZ")-on-miss behavior is a deliberate deviation from reading it on every lookup: it trades up-to-1s-late TZ detection for avoiding the slowgetenvper call. Happy to restore per-lookup detection if you'd prefer.