github ↗
docs menu

Module Rig.Profile

Profiles of devices' work.

A profile of every device is taken while a function runs (Profile.take). Profiles nest and overlap, in any domains, and each holds the events recorded while it is taken. Every time is on the host clock (Profile.now). When no profile is being taken, recording costs one atomic read and allocates nothing. Taking a profile may slow the submits of a device whose driver times its work, as the driver's interface states.

#type device := t
#type event =
  1. #| Work of {
    1. #point : Point.t;
      (*

      The submit's point (submit).

      *)
    2. #device : device;
    3. #lane : string;
      (*

      Its track within the device.

      *)
    4. #name : string;
    5. #start : int;
    6. #stop : int;
    }
    (*

    A submission's work on a lane of its device, from when the device started it to when the device ended all its parts' work, on the lane of the queue its first part ran on, such as "COMPUTE:0" (Rig_edge.queue). Each submission with parts records it. It is named after the functions its launches run, in the order of its parts, joined by ", ", or, with no launch, after its first part's kind, "copies", "fills" or "words". A device whose driver cannot time a submission's work records none of it, nor does one whose driver finds the work's converted times outside the host's window around it, from the submit to their conversion, by more than the driver states.

    Its driver converts the device's clock to the host's through measures of one against the other. A time's error is the deviation of the measures it was converted with, as each driver states, plus the error of extrapolating since the latest.

    *)
  2. #| Span of {
    1. #device : device;
    2. #lane : string;
      (*

      Its track within the device.

      *)
    3. #name : string;
    4. #start : int;
    5. #stop : int;
    }
    (*

    Host code that span timed, on the calling domain's lane of host: "domain 0", "domain 1", ….

    *)
  3. #| Allocation of {
    1. #device : device;
    2. #time : int;
    3. #allocated : int;
    }
    (*

    The bytes of memory device allocated from time on.

    *)
  4. #| Load of {
    1. #image : Image.t;
    2. #binary : string;
    3. #time : int;
    }
    (*

    An image loaded.

    *)
  5. #| Counters of {
    1. #device : device;
    2. #name : string;
    3. #start : int;
    4. #stop : int;
    5. #counters : (string * int array) list;
      (*

      Each counter's name and its count during the run, one per unit of the hardware that counts it.

      *)
    }
    (*

    The counters of a run of a function.

    *)
  6. #| Trace of {
    1. #device : device;
    2. #name : string;
    3. #start : int;
    4. #stop : int;
    5. #part : int;
      (*

      The index of the device's unit that wrote it.

      *)
    6. #data : string;
      (*

      The trace as the device wrote it.

      *)
    }
    (*

    The thread trace that a unit of a device wrote during a run.

    *)
  7. #| Unmeasured of {
    1. #device : device;
    2. #time : int;
    3. #runs : int;
    }
    (*

    runs runs of functions on device, submitted while a profile was taken, whose counters its driver could not measure, as the driver's interface states. time is the end of their submission's work, or when rig read them where the device could not time that work.

    *)
  8. #| Copy of {
    1. #src : device;
    2. #dst : device;
    3. #bytes : int;
    4. #point : Point.t option;
      (*

      Some p for a copy a device runs, p its submission's point, and None for a copy the host or an io device runs.

      *)
    5. #start : int;
    6. #stop : int;
    }
    (*

    A transfer of bytes from src's memory to dst's: a copy part of a submission (Submission.copy), rig's own and those of a Buffer.submit_copy among them, or a copy the host or an io device runs. A box (Buffer.box) records the bytes of its rows. src and dst are the devices in whose allocated count the memory the bytes leave and reach lies (Reclamation), or the device whose memory it is where it counts in none. So a Buffer.borrow names the device whose memory it borrows, and Pinned memory the device whose budget it counts in.

    Each copy part of a submission records an event with the times of the submission's Work where its device times the work. Otherwise the event starts when the copy was asked, at the submit. It ends when the copy ended where the submit runs it, as for a copy the host, an io device or a memory_device runs, and otherwise when take reads that it is done, so the event then lasts at least as long as the copy. A copy through staging memory records each piece into and out of it, with host as one side, and no event of its own. A fill (Buffer.submit_fill) moves no bytes from memory and records none: where a device runs it, its submission's Work is named "copies".

    *)

The type for profile events. Times are nanoseconds of the host clock. rig records a submission's work, spans, allocations, loads and copies. A device whose driver measures its launches' counters records Counters and Unmeasured events, as its driver's interface states. A library whose compiled code measures its work, such as with a device's thread traces, records Trace events (record).

#val take : (unit -> 'a) -> 'a * event list

take f is f () and the events recorded while it ran, in time order and, at equal times, longest first, then in the order they were recorded. Before it returns it waits for the work whose events are still to be read, such as the submissions made while it ran. If f raises, take discards the events and raises the exception again with its backtrace.

Raises Lost naming a device whose events are still to be read if it is lost before they are read, having discarded the events. That device may be any whose work was recorded while f ran, another domain's included. A close first reads its device's events of completed work, so a device that f closes raises nothing. An event that another domain's take has begun to read when the close starts stays that take's, which then raises the close's Lost.

#val enabled : unit -> bool

enabled () is true iff some profile is being taken.

#val now : unit -> int

now () is the host clock, in nanoseconds of the system's monotonic clock from an unspecified start.

#val record : event -> unit

record e adds e to each profile being taken when it is called, so take returns it among rig's own events, in time order. It records nothing while no profile is being taken, so counters a library reads after take has returned join no profile, even one of the work they count.

#val span : string -> (unit -> 'a) -> 'a

span name f is f (), recorded as a span named name on the calling domain's lane of host, from the call until f returns or raises, in each profile taken when the call starts.

#val output_chrome_trace : Stdlib.out_channel -> event list -> unit

output_chrome_trace oc events writes events to oc in Chrome's trace event format, JSON, which Perfetto and chrome://tracing load. It writes a process per device, a thread per lane, a complete event per work and span, per copy (on its src's process) and per run's counters (each summed over its units), a counter memory per allocation change, and instant events for loads, traces and unmeasured runs. Times are microseconds from the earliest event. oc is neither flushed nor closed.