Skip to content

Timing Clocks and Timers

OpenAI Codex edited this page Nov 1, 2026 · 4 revisions

Clocks & Timers

Reviewed: 2026-11-01 · Source: b7140848 · Release availability: #267/#271 included in September 30 stable.

xemu has several layers of timing. A host operating-system clock, a QEMU clock domain, an emulated device clock, and a guest-visible timer register are related, but they are not the same thing.

This distinction matters for both correctness and performance. Changing host wait precision is different from changing the virtual clock; changing the virtual clock is different from changing an NV2A PLL; and a timer callback is different from an interrupt becoming visible to the guest CPU.

Timing stack

flowchart TD
    A[Host clock / OS wait primitive] --> B[QEMU clock domain]
    B --> C[QEMUTimerList]
    C --> D[QEMUTimer deadline]
    D --> E[Timer callback]
    E --> F[Emulated device state]
    F --> G[Guest-visible counter / pending bit / IRQ]
    G --> H[vCPU observes device effect]
Loading

The same timer infrastructure can support many device models, but each device decides which clock domain it uses and what its callback means.

QEMU clock domains

util/qemu-timer.c implements the core clock and timer-list infrastructure used by xemu.

Current clock types include:

Clock Purpose
QEMU_CLOCK_REALTIME Host real-time progression for operations tied to wall/monotonic host passage rather than guest execution
QEMU_CLOCK_VIRTUAL Virtual machine time used by emulated hardware that should follow the guest machine's virtual execution timeline
QEMU_CLOCK_HOST Host clock domain for host-facing timing behavior
QEMU_CLOCK_VIRTUAL_RT Virtual real-time-oriented clock behavior used by QEMU infrastructure where appropriate

The exact clock chosen is part of a device's semantics. Replacing one with another can change pause behavior, snapshot behavior, deadline ordering, and how quickly a guest-visible device advances.

QEMUClock, timer lists, and timers

The timer system separates the clock from the timers scheduled against it.

Object Role
QEMUClock Defines a time domain and current time source
QEMUTimerList Ordered collection of deadlines for one clock
QEMUTimerListGroup Groups timer lists for event-loop deadline calculations
QEMUTimer Callback plus expiration time and owning list

Common operations include:

  • qemu_clock_get_ns() — read a clock domain.
  • timer_new_ns() / timer_new_us() / timer_new_ms() — create timers with convenient units.
  • timer_mod() — arm or move a timer to a deadline.
  • timer_mod_anticipate() — move a deadline earlier when appropriate without needlessly delaying an existing earlier event.
  • timer_del() — remove an armed timer.
  • qemu_clock_run_timers() / timer-list processing — execute callbacks whose deadlines have expired.

Deadline insertion and wakeups

A timer list is not just a sorted container. If code inserts a new deadline that is earlier than what the event loop was expecting, the event loop may need to be notified so it recalculates its wait.

Conceptually:

Device computes desired deadline
    ↓
timer_mod(...)
    ↓
Timer list ordering changes
    ↓
Is new earliest deadline sooner than current wait?
    ├─ no  → no urgent host wakeup
    └─ yes → notify event loop

This is one reason unnecessary timer re-arming can matter even when the final guest-visible time is unchanged: list mutation, locking, notifier traffic, and host wakeups are work in their own right.

Device timer lifecycle

sequenceDiagram
    participant D as Device model
    participant Q as QEMU timer list
    participant E as Event loop
    participant C as Timer callback
    participant G as Guest-visible state

    D->>Q: timer_mod(deadline)
    Q->>E: Notify if earliest deadline changed
    E->>Q: Run expired timers
    Q->>C: Invoke callback
    C->>G: Update pending state / IRQ / registers
Loading

A callback should generally re-arm itself only if the emulated hardware requires another future event. Repeatedly deleting and re-inserting an unchanged deadline can be semantically correct but unnecessarily expensive.

Xbox platform timers

The Xbox machine also includes PC-compatible timing devices initialized as part of the platform, including PIT and RTC functionality. Those devices are separate from the NV2A's internal PTIMER.

The key rule is: do not infer one guest timer's behavior from another timer merely because both eventually use QEMU's timer APIs.

NV2A PTIMER

hw/xbox/nv2a/ptimer.c implements the NV2A timer block visible through GPU registers.

The PTIMER counter is derived from:

  1. QEMU_CLOCK_VIRTUAL nanoseconds.
  2. NV2A core-clock configuration from PRAMDAC.
  3. PTIMER numerator/denominator scaling.

At a conceptual level:

QEMU virtual nanoseconds
        ↓
NV2A core clock frequency
        ↓
PTIMER numerator / denominator
        ↓
Guest-visible PTIMER tick value

This means PTIMER is not simply a copy of the host high-resolution timer.

PTIMER guest state

The device exposes state corresponding to items such as:

  • Clock numerator.
  • Clock denominator.
  • Current time low/high registers.
  • Alarm value.
  • Interrupt pending state.
  • Interrupt enable state.

The current timer value is computed from virtual time rather than being maintained by incrementing a host counter for every guest tick.

PTIMER alarm path

flowchart TD
    A[Guest writes PTIMER alarm/control] --> B[Compute timer state]
    B --> C[Convert target PTIMER tick to virtual deadline]
    C --> D[Arm / adjust QEMU timer]
    D --> E[Virtual deadline expires]
    E --> F[PTIMER callback]
    F --> G[Update PTIMER pending state]
    G --> H[update_irq]
    H --> I[Guest eventually receives NV2A interrupt]
Loading

The conversion in both directions matters. If integer math rounds a future guest tick down to an earlier host/virtual deadline, a timer can fire too soon. Conversely, overly conservative rounding can delay a guest event.

PTIMER versus PRAMDAC clock changes

Because PTIMER derives its rate from NV2A clock configuration, changing the relevant PRAMDAC clock can change the relationship between virtual nanoseconds and PTIMER ticks.

A robust implementation therefore needs to distinguish:

  • Guest-visible PTIMER count continuity.
  • Current hardware clock ratio.
  • The already scheduled host/virtual deadline.
  • Whether a clock change requires recomputing that deadline.

This is also a snapshot concern: a restored guest should observe coherent device time even though the host process resumes at a different real-world moment.

Host high-resolution waiting

On Windows, xemu/QEMU can use higher-resolution host timing behavior when the relevant setting is enabled. This affects how accurately the process can wait or poll for a host deadline.

It does not mean:

higher host timer resolution
    =
faster guest PTIMER

The relationship is instead:

Guest/device deadline
    ↓
QEMU schedules host work for that virtual/host point
    ↓
OS wait precision determines how accurately the process wakes

Host timing precision can reduce oversleep or jitter but may have CPU/power tradeoffs. It should be evaluated as a scheduling mechanism, not as a virtual CPU overclock.

Pausing and virtual time

Virtual-clock semantics are important when emulation pauses. A hardware timer that is defined in virtual-machine time should generally not advance simply because the user left the emulator paused for ten real-world minutes.

This is one reason xemu cannot collapse every timer onto host wall time. The correct choice depends on what the original hardware would expose to the guest.

Snapshot behavior

Timer state has two kinds of information:

Type Example Migration rule
Guest architectural state Alarm register, enable bit, pending bit Must be restored according to device VMState semantics
Derived host scheduling state Position in host timer list, OS wait handle Reconstruct from restored guest/device state rather than treating host object identity as guest state

After load, the emulator may need to recompute the next timer deadline from restored registers and the restored virtual-time relationship.

Timer performance costs

Timer performance problems are often misdiagnosed because the callback itself may be cheap while scheduling behavior is expensive.

Potential cost areas include:

Area Possible cost
Clock reads Frequent calls in hot paths
Timer-list mutation Insert/remove/reorder operations and locking
Event-loop notification Waking a thread because an earlier deadline was inserted
Callback frequency Host work repeated more often than guest semantics require
IRQ transitions Cross-subsystem CPU/device work after callback
Oversleep / wake jitter Tail latency when host scheduling misses a desired deadline
Busy polling Lower latency at the cost of host CPU consumption

The useful metric is therefore not just “number of timers.” Measure list operations, callbacks, IRQ changes, host waits, and resulting guest progress separately.

Timing fidelity differs by device

A QEMU timer is used only where the device model needs a future event. Many register-level models intentionally complete synchronously.

Device/path Current timing model Important implication
NV2A PTIMER Derived from virtual time + PRAMDAC/PTIMER scaling; alarm scheduled with QEMU timer Guest-visible clock/deadline relationship is modeled
NVNet PHY autonegotiation 250 ms QEMU_CLOCK_VIRTUAL deadline Coarse link negotiation delay is preserved
SMBus transfer Target callback runs synchronously from START write Physical SCL/SDA transfer time omitted
ACPI GPIO field pin Static bit toggles on each guest read Apparent field frequency depends on polling, not video timing
MCPX APU frame pacing Dedicated thread + SDL queued bytes + QEMU_CLOCK_REALTIME Host audio backend affects pacing/waits
SMC power request Register write directly requests reset/shutdown PIC firmware response delay omitted
Video encoders Register storage Encoder scan/analog timing is not implemented there
flowchart TD
    A[Guest observes time-dependent behavior] --> B{Model type}
    B -->|virtual deadline| C[QEMU virtual clock / timer]
    B -->|host pacing| D[realtime clock / host backend]
    B -->|synchronous abstraction| E[immediate callback/result]
    B -->|polling approximation| F[result changes when read]
Loading

This distinction should be recorded in any timing benchmark. A path with no timer callbacks may simply be modeled synchronously; it does not prove the corresponding physical hardware had zero latency.

Current PTIMER and PGRAPH control boundary

Reviewed main and the September 30 stable include #267 PTIMER reconciliation and #271 PGRAPH interrupt/control decoupling. The first reconciles timer semantics and queue scheduling; the second prevents guest control/interrupt work from waiting behind renderer compilation/work while holding BQL. This restores observed responsiveness and progress rather than increasing the guest clock rate.

The retained Windows Vulkan Def Jam diagnostic reaches an active match beyond the prior freeze: 10,266 callbacks against 10,267 alarm writes, maximum lateness 2.406 ms and maximum BQL-held MMIO wait 2.826 ms. It exits 0 after about 13.8 minutes; the planned hour, OpenGL and save/load qualification were not completed. Operational evidence and boundaries.

BEFORE: guest control/interrupt observation -> renderer dependency under BQL
AFTER:  guest control/interrupt state -> independent current control path
        renderer work retains its own ownership and completion rules

The earlier unmerged #81 deadline-state experiment is historical research. Its keep/cancel/arm design, phase-rounding discussion and adverse OpenGL Morrowind observations are not the current implementation story or accepted performance proof. Keep the guest clock, virtual deadline, callback, pending IRQ and vCPU observation distinct when reasoning about either design.

Debugging a timing problem

A practical timing investigation should answer these questions in order:

  1. Which guest-visible clock or timer is involved?
  2. Which QEMU clock domain drives it?
  3. Is the observed error in counter conversion, deadline conversion, host waiting, callback execution, or interrupt delivery?
  4. Does the problem occur while the VM is paused or only while running?
  5. Does snapshot restore reproduce the same phase/deadline?
  6. How many actual timer_mod / timer_del / callback operations occur?
  7. Is a performance change reducing useful work or merely moving a wait elsewhere?

Source map

Source Responsibility
util/qemu-timer.c QEMU clocks, timer lists, deadlines, timer execution, host timing integration
hw/xbox/xbox.c Xbox platform timer-device setup such as PIT/RTC
hw/xbox/nv2a/ptimer.c NV2A PTIMER counter, alarm, interrupt state and scheduling
hw/xbox/nv2a/pramdac.c NV2A clock configuration that influences PTIMER rate
hw/xbox/nv2a/nv2a.c NV2A lifecycle, interrupt integration, migration state

Related Development

  • PR #267 / #271: delivered PTIMER reconciliation and interrupt/control decoupling; stable availability and bounded evidence above.
  • PR #81 — NV2A: replace PTIMER host scheduling with explicit deadlines: historical unmerged deadline/queue experiment; original qualification remains separate.
  • PR #59: archived earlier PTIMER semantic investigation used as historical input to the newer work.

See Development for current status.

Debugging timer-driven lag

Debugging Lag & Hot Paths describes how timer/main-loop wakeups are correlated with guest progress before changing deadlines or host waiting. Fewer timer calls or more precise host waits are not accepted as a speedup without matched Release evidence.

Related pages

Clone this wiki locally