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§
Sourcefn launched(&self, kernel: &'static str)
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§
Sourcefn timing(&self) -> TimingRequest
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
Resolvedaround 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.
Sourcefn profiled(&self, _kernel: &'static str, _profile: ProfileDuration)
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.
Sourcefn timed(
&self,
_kernel: &'static str,
_duration: Duration,
_method: TimingMethod,
)
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".