Performance

Elide implements the [WinterCG Minimum Common API][wintercg]'s performance surface — the [Performance][hr-time] interface, [User Timing][user-timing] (mark, measure, PerformanceMark, PerformanceMeasure), and [Performance Timeline][perf-timeline] (PerformanceEntry, PerformanceObserver, PerformanceObserverEntryList). A single realm-bound timeline backs both globalThis.performance and the Node-style node:perf_hooks module — every entry is observable through both surfaces.

[wintercg]: https://min-common-api.proposal.wintercg.org/ [hr-time]: https://w3c.github.io/hr-time/ [user-timing]: https://w3c.github.io/user-timing/ [perf-timeline]: https://w3c.github.io/performance-timeline/

globalThis.performance

Singleton instance of Performance, installed on every realm at agent init. Inherits from EventTarget, so addEventListener / removeEventListener / dispatchEvent work on it (Performance has no built-in events of its own — the inheritance is for guest extensions).

ts
declare const performance: Performance;

interface Performance extends EventTarget {
  readonly timeOrigin: DOMHighResTimeStamp;
  now(): DOMHighResTimeStamp;
  mark(name: string, options?: PerformanceMarkOptions): PerformanceMark;
  measure(
    name: string,
    startOrOptions?: string | PerformanceMeasureOptions,
    endMark?: string,
  ): PerformanceMeasure;
  getEntries(): PerformanceEntry[];
  getEntriesByName(name: string, type?: string): PerformanceEntry[];
  getEntriesByType(type: string): PerformanceEntry[];
  clearMarks(name?: string): void;
  clearMeasures(name?: string): void;
  toJSON(): object;
}
Performance is not constructible from JavaScript. new Performance() and plain-call Performance() raise TypeError.

Time

  • performance.timeOrigin — the wall-clock millisecond timestamp at which the realm was created. [SameObject]: identical value across reads.
  • performance.now() — high-resolution time in milliseconds since timeOrigin. Resolution is limited only by the host clock (typically ≥ 1 µs); the spec only mandates ≥ 5 µs.

mark(name, options?)

Records a PerformanceMark and registers it for later reference by name in measure(...).

options.startTime, when supplied, is used verbatim as the mark's startTime; otherwise the current now() is captured. Negative startTime raises TypeError. options.detail is [structured-cloned][clone] at construction time — subsequent mutation of the source value does not bleed into the mark. The clone subset is the same one documented for postMessage: primitives, plain objects, dense arrays, and cycles. Unsupported types raise DataCloneError DOMException.

[clone]: https://html.spec.whatwg.org/multipage/structured-data.html#structured-clone

measure(name, startOrOptions?, endMark?)

Records a PerformanceMeasure between two anchors. The second argument can be:

  • omitted / undefined — span from 0 to now();
  • a string — name of a previously-recorded mark; span from that mark's startTime to either endMark (if supplied) or now();
  • a PerformanceMeasureOptions dictionary with any two of start / end / duration (passing all three raises TypeError); each may itself be a mark name or a numeric timestamp. The dictionary may also carry a structured-cloned detail.

Passing both an options dict and a third endMark argument raises TypeError. Referencing a mark name that does not exist raises SyntaxError DOMException.

Reading the timeline

getEntries() returns every recorded entry sorted by startTime ascending (a fresh array per call). getEntriesByName(name, type?) and getEntriesByType(type) filter the same set.

Clearing

clearMarks(name?) and clearMeasures(name?) remove matching entries. Passing no name clears everything of that type. The mark-name registry used by measure(name, ...) is cleared in lock-step.

PerformanceEntry

Abstract base for PerformanceMark, PerformanceMeasure, and (Node-only) PerformanceNodeTiming. Not constructible from JavaScript. Exposes:

ts
interface PerformanceEntry {
  readonly name: string;
  readonly entryType: "mark" | "measure" | "node";
  readonly startTime: DOMHighResTimeStamp;
  readonly duration: DOMHighResTimeStamp;
  toJSON(): { name; entryType; startTime; duration };
}
toJSON() writes the four standard attributes; detail is intentionally omitted (mirrors Node's behaviour).

PerformanceMark

Constructible:

ts
new PerformanceMark(name: string, options?: PerformanceMarkOptions): PerformanceMark

Constructed marks are recorded in the per-realm timeline, so a hand-built mark is visible through performance.getEntries(). mark.duration is always 0. mark.detail is the structured-cloned snapshot from options.detail, or null.

PerformanceMeasure

Not constructible from JavaScript — performance.measure(...) is the only producer. Exposes the inherited entry surface plus detail (structured-cloned snapshot of the PerformanceMeasureOptions.detail dictionary entry, or null).

PerformanceObserver

ts
new PerformanceObserver(
  callback: (list: PerformanceObserverEntryList, observer: PerformanceObserver) => void,
): PerformanceObserver

interface PerformanceObserver {
  observe(options: { entryTypes?: string[]; type?: string; buffered?: boolean }): void;
  disconnect(): void;
  takeRecords(): PerformanceEntry[];
}
PerformanceObserver.supportedEntryTypes: ReadonlyArray<"mark" | "measure" | "node">;

Callback delivery is asynchronous — buffered entries fire at the end of the next microtask checkpoint. Inside the callback, list.getEntries() / list.getEntriesByName / list.getEntriesByType walk a frozen snapshot.

observe(options) accepts either entryTypes (array of strings) or type (single string), not both, not neither — passing any other shape raises TypeError. Switching modes between calls (from entryTypes to type or vice versa) on the same observer also raises TypeError. buffered: true pre-populates the observer's buffer with entries already on the timeline that match the requested types — useful for observers attached after the work being observed has already completed. disconnect() removes the observer from the realm's dispatch list and drops its buffer. takeRecords() synchronously drains the current buffer and returns the entries; subsequent deliveries are unaffected. PerformanceObserver.supportedEntryTypes is a frozen array of the entry-type strings this implementation recognises: "mark", "measure", "node". [SameObject]: same array reference across reads.

PerformanceObserverEntryList

Argument passed to the observer callback. Holds an immutable snapshot of the entries being delivered. Same getEntries / getEntriesByName / getEntriesByType surface as Performance; results are filtered against the snapshot, not the realm timeline.

Notes on the implementation

  • Time origin is captured once at agent install via System.currentTimeMillis() for the user-observable timeOrigin and System.nanoTime() for the monotonic clock that powers now(). The two are taken at the same instant so the relationship now() = wallclock - timeOrigin holds.
  • Observer dispatch hooks into the realm's microtask checkpoint listener (the same hook the WinterCG unhandledrejection flow uses) — there is no separate task queue. A faulty observer callback is contained: it does not block other observers from being notified at the same checkpoint.
  • The Node-only PerformanceNodeTiming entry shows up on the same timeline; see the node:perf_hooks page for its full attribute list and timing semantics.