Skip to main content

LaunchObserver

Trait LaunchObserver 

Source
pub trait LaunchObserver: Send + Sync {
    // Required method
    fn launched(&self, kernel: &'static str);

    // Provided methods
    fn timing(&self) -> TimingRequest { ... }
    fn profiled(&self, _kernel: &'static str, _profile: ProfileDuration) { ... }
    fn timed(
        &self,
        _kernel: &'static str,
        _duration: Duration,
        _method: TimingMethod,
    ) { ... }
}
Expand description

Notified of every kernel launch, on the thread that issued it.

Implementations must be cheap, must not launch, and must not install or drop a LaunchObservation: this runs inside the launch path, before the kernel reaches the server, and holds the lock that guards the installed observer.

Required Methods§

Source

fn launched(&self, kernel: &'static str)

A kernel was issued, named as the kernel names itself. Pass it through type_name_format to shorten it the way the profiling logger does.

Provided Methods§

Source

fn timing(&self) -> TimingRequest

Whether each launch should be timed, and which of profiled and timed its measurement reaches.

None by default, because timing is not free. Bracketing a launch with profile markers costs the issuing thread a round trip to the server per kernel, and Resolved also blocks until the kernel has run, removing the overlap between kernels. An observer that only wants to know which kernels ran should leave this alone; one measuring where a pass spends its time wants Deferred.

Two situations refuse the measurement without refusing the launch:

  • A profile the server cannot take — a graph capture window refuses them on the spot. The kernel is still launched, still reported to launched, and the measurement is skipped for it, with a warning in the log.
  • Don’t ask for Resolved around collective kernels. Reading blocks until the kernel completes, and a collective completes only when its peers launch — a thread that issues more than one side of a collective deadlocks waiting for the first.
Source

fn profiled(&self, _kernel: &'static str, _profile: ProfileDuration)

A kernel was timed, and this is its measurement — not yet read back. Keep it, and read it once the work being measured is over.

Only called under TimingRequest::Deferred, on the thread that issued the launch, right after it. Where the backend times on the device without waiting — CUDA, HIP, and wgpu’s timestamp queries — the measurement is two events in the stream that nothing has waited for, and the resolved ProfileTicks carry the window’s start and end on one clock, so an observer can also say where the device sat idle between kernels. Metal waits for the window and places it at the moment it was read: its lengths are device time, its starts and ends do not line up, and keeping the measurement saves nothing there.

Called under the same lock as every other method here, so keeping the measurement must be cheap. Reading it — ProfileDuration::resolve — blocks until the device has reached both events, so doing that here would hold the lock for the length of the kernel, which is the thing Deferred exists to avoid.

The default drops it: an observer declaring Deferred owes an implementation.

Source

fn timed( &self, _kernel: &'static str, _duration: Duration, _method: TimingMethod, )

A kernel finished, and took this long.

Called under TimingRequest::Resolved once the launch path has read the measurement — and under TimingRequest::Deferred too, in place of profiled, whenever the profiling logger is reading measurements as well. So an observer that asked to keep measurements unread still has to implement this, or it loses every timing whenever the logger is on.

It arrives after the launch rather than before it, so an observer pairing kernels with its own state should do that in launched and use this only for the duration. A duration goes to the observer the launch was reported to, and only while it is still installed: an observation that ends mid-read is not told.

method is not a detail. A backend falls back to System where it cannot get a device timestamp — wgpu does exactly that once the timestamp-query budget is spent — and a system timing is host wall around a blocking submit, which includes submission, sync, and the kernel’s compilation on its first launch, rather than the kernel. The two are not the same measurement and an observer reporting them as one will show a number that moves several-fold between runs.

Dyn Compatibility§

This trait is dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementors§