Module Rig.Submission
Prepared submissions.
A submission is work for one device, made of parts, such as copies and launches. It is made once and run many times. The buffers and the points to wait for change from one run to the next, so they are arguments of submit, and so are the launch parameters, which a Submission.Run holds. Any number of domains or threads submit one submission concurrently, each through a run that it alone has taken while it stores and submits (Submission.take).
A submission's fixed memory is the buffers that every run uses and that neither a part nor a submit names, such as an argument buffer and the memory its addresses name. Each is fixed with an access, and each submit orders it by that access. A buffer may be fixed in any number of submissions.
Rule 2 of Order orders a submission's parts by the memory they use. A copy reads its src and writes its dst, words read their buffer, a buffer ref uses its slot as its kind says, and every part uses the fixed memory with its access. Two uses may share memory where their buffers share a word (Buffer.overlaps), or where either is a slot, whose buffer only a run gives. A Submission.unordered ref orders nothing.
A launch or a fill runs on the first of the device's queues that runs its kind (Rig_edge.facts). Words run on the first queue that runs both launches and words, and otherwise on the first that runs words. A copy runs on the queue of the last earlier part it follows, where that queue runs copies, and otherwise on the device's copy engine, the queue that copy_engine names in its facts.
A submission keeps every buffer and image its parts name, its fixed memory and its keep reachable while it is reachable, so that memory is never freed between Submission.make and a submit. A launch needs no keep for its image, since an image unloads only once the work submitted before the image became unreachable is done (Image.load). Every run reads its fixed memory and the buffers its parts name, so it holds them for its life as Claim.hold does: no donation writes them and no consumption ends them. Claims says when it claims the buffers of a run.
type tThe type for prepared submissions.
Refs
type refThe type for a launch's or a fill's references to a submit's buffers and to functions. A ref places a value in the 8 parameter bytes at at when the work runs. at must be a multiple of 8 and its 8 bytes must lie among the part's parameters, and two refs of a part must not share an at.
A buffer ref names a slot, an index of submit's buffers. Its 8 bytes hold a byte offset into the slot's buffer, and when the work runs they hold that offset plus the address of the buffer's first byte, as the device's work addresses it. Through a buffer ref the work may address only the words that hold that buffer's bytes (Words), and write them only through a write or an unordered ref. rig orders the work by its buffer refs and checks neither rule.
read, write and unordered raise Invalid_argument if slot or at is negative or at is no multiple of 8, and entry if at is.
read slot ~at is a ref through which the work reads the buffer slot.
write slot ~at is a ref through which the work reads and writes the buffer slot, which must admit writes (Buffer.access).
unordered slot ~at is a ref through which the work reads and writes the buffer slot at any time, ordered only by device order, the exception to rule 3 of Order. The buffer must admit writes.
val entry : Image.func -> at:int -> refentry fn ~at is a ref whose 8 bytes hold, when the work runs, the value by which the device's work refers to fn (Image.entry). rig writes it over the run's bytes there. The submission keeps fn reachable, so the work may call fn through it. fn's image must be loaded on the device.
Parts
type partThe type for parts: a piece of a submission's work.
type side = The type for the ends of a copy.
copy ~src ~dst copies src's bytes into dst. The two buffers must have one length and share no byte of memory, and dst must admit writes. Each buffer must be on the device (Buffer.borrow puts another device's memory there). On a driver's device of another machine, one of the two may instead be memory this process's host addresses, whose bytes the device's driver carries. A slot's buffer may be such memory only if copies alone name the slot. make checks where the ends that are buffers are, and each submit checks the ends that are slots, with its buffers.
Raises Invalid_argument if src and dst are one slot or a slot is negative, a buffer end is dead, dst is a buffer that admits only reads, or both ends are buffers and they differ in length or share a byte.
val launch : ?refs:ref array -> Image.func -> params:int -> partlaunch ~refs fn ~params runs fn once, over the grid of groups of threads that its block in the run sets (block), with the params bytes of parameters of that block and each of refs (defaults to [||]) placed (ref), which launch reads once. fn's image must be loaded on the device.
Raises Invalid_argument if params is negative or above 2{^31} - 32, the most one block of a run holds (make), or is fewer than the bytes fn reads where its driver knows them (Image.params); or if two refs share an at, or a ref's 8 bytes lie past params or past the bytes fn reads.
fill ~refs f ~params calls the C function at f from the driver's submit (The C functions) with the queue's context, the host address of its argument and the work's value. The argument is a copy of the params bytes of parameters of the fill's block in the run (block), with each of refs (defaults to [||]) placed (ref), and is valid only during the call. The function must call no function of the OCaml runtime, since the driver's submit may call it with the runtime released. It returns 0, or another value for a failure, which loses the device.
Raises Invalid_argument if params is negative or above 2{^31} - 32, or if two refs share an at or a ref's 8 bytes lie past params.
words b places the 32-bit words of b on the device's queue, such as indirect-buffer or GPFIFO entries. Words may do what rig cannot see, such as start a thread trace or invalidate caches before a launch, so rule 2 of Order orders them after every earlier part, and Rig.Submission's preamble says which queue runs them. Memory the words address is ordered against other work only if it is among the submission's fixed memory. A driver may bound the words of one submission (Valid submissions).
Raises Invalid_argument if b is dead, the host does not address its memory, or its length is no multiple of 4.
Making
make ~keep ~fixed d parts is a submission of parts on d. Its slots are 0 to n - 1, where n is one more than the highest slot its buffer refs and copies name, and each run passes one buffer per slot (submit). One buffer may fill several slots. fixed (defaults to []) is the submission's fixed memory. make reads parts once, and no bytes of the buffers, so a caller may write addresses into them afterwards.
keep is a value the work uses beyond memory, such as the driver objects that a graph or an indirect command buffer made. The submission keeps it reachable while it is reachable. Once the submission is collected, keep goes as Reclamation states, or, on a lost d and in a child of fork, as Stops and forks states. keep orders no memory. Rig.Submission's preamble states how the parts are ordered and which queue runs each.
Raises Invalid_argument if the submission would not be valid (Valid submissions), and Lost if d is lost.
address s b is the address of b's first byte as the work of s's device addresses it, where b's bytes lie within a buffer of s's fixed memory. A caller writes it into s's blocks or into memory s fixes, such as an argument buffer or a replay's commands, before the submit that reads it. An empty buffer that Buffer.create made on a driver's device has the address 0. An address fits in the 62 bits of an int's non-negative range.
Raises Invalid_argument if b is dead or handle-only memory (Low level), or no buffer of s's fixed memory holds b's bytes.
take s is a run of s that the calling thread has taken for its stores and its next submit: the first of s's runs that is free, or else a new one, whose bytes read 0. A take makes a run only when every run it tried was taken. s keeps its runs for its life, and take allocates nothing when it finds a free one. A caller that is s's only taker and takes one run at a time gets the same run every time, which keeps its stores.
A run holds what its last taker stored, so any other caller must store every byte its submit reads, or its work runs with another caller's grid or parameters. submit frees the run, and a caller that raises before its submit must release it (Run.release). A run stays taken until its thread releases or submits it: one taken by a thread that has ended stays taken, as does, in a child of fork, one that another thread of the parent had taken. A submission with no launch or fill has one run, holding no bytes, which every take returns and any number of submits use concurrently.
Raises Stdlib.Out_of_memory if the host has no memory for a new run.