docs / stdlib / xpu

Stdlib index
  1. Overview
  1. Captured-borrow migration worklist (3.3.3)
  2. Owned-bind migration worklist (8.2.7)
  3. Return-side title audit — the ride-through enumeration
  4. stdlib ownership audit
  5. stdlib ownership dispositions (plan 1.3.1 / 4.3.1)

aot

  1. Components
  2. Scope
  3. Scoped

codec

  1. Base32
  2. Base64

codec / csv

  1. Csv

codec / json

  1. Json

collection

  1. ArrayList
  2. BPlusTree
  3. Cache
  4. Collectors
  5. HashMap
  6. HashSet
  7. Heap
  8. ImmutableList
  9. ImmutableMap
  10. ImmutableSet
  11. LinkedList
  12. RedBlackTree
  13. Sort

collection / ltm

  1. LtmBPlusTree

concurrent

  1. AtomicInt32
  2. AtomicInt64
  3. Channel
  4. Fiber
  5. FiberLocal
  6. Lock
  7. Mutex
  8. RwLock
  9. Semaphore
  10. Tasks

error

  1. Exception
  2. NoOptionalValueException
  3. NotProvidedException
  4. RecoverableException
  5. ScopedBuildFailedException
  6. ScopeNotActiveException
  7. Throwable
  8. UnrecoverableException

gfx

  1. Sampler
  2. Texture2D

hash

  1. DefaultHasher
  2. Hash
  3. SipHash
  4. XXHash3

ifx

  1. BackendRegistry
  2. Window

io

  1. Buffer

io / file

  1. File
  2. FileInfo
  3. FileReader
  4. FileWriter
  5. Path
  6. Watcher

io / net

  1. BufferPool
  2. ByteBuffer
  3. IpAddress
  4. Server
  5. ServerBuilder
  6. SocketAddress
  7. TcpListener
  8. TcpStream
  9. UdpSocket

io / net / dns

  1. Dns

io / net / uri

  1. Uri
  2. UriBuilder

lang

  1. Guid
  2. Math
  3. Optional
  4. Pair
  5. Slice
  6. String
  7. StringBuilder

lang / stream

  1. ArrayStream
  2. Stream

math

  1. Camera
  2. Color
  3. DType
  4. Ray
  5. Rotation
  6. Tensor
  7. Transform

math / fft

  1. Fft

math / linalg

  1. LinAlg

math / npio

  1. Npy

math / poly

  1. Poly

math / random

  1. Generator

math / stats

  1. Stats

nucleo

  1. Columns — the Arrow-laid-out substrate
  2. Fused tensor expressions — Fuse
  3. Table — the lazy, typed dataframe
  4. Tape — define-by-run autograd
  5. Transform intrinsics — Grad, Vmap, Jit

process

  1. Command
  2. Process

reflect

  1. Class

search / distance

  1. Distance

search / fuzzy

  1. Matcher

search / ngram

  1. Index

session

  1. PackageInstallException
  2. Packages

time

  1. Clock
  2. DateTimeFormatter
  3. Duration
  4. Instant
  5. LocalDate
  6. LocalDateTime
  7. LocalTime
  8. Period
  9. ZonedDateTime
  10. ZoneId
  11. ZoneOffset

wire

  1. Compressor
  2. CompressStream
  3. DecompressionLimitException
  4. Decompressor
  5. Encoder
  6. Schema
  7. SchemaEncoder

xpu

  1. Device
  2. KernelBuffer
  3. KernelStream
  4. KernelTimer

xpu / mesh

  1. MeshSimplifier

KernelStream

cajeta.xpu.KernelStream — ordered queue of XPU work: a backend-tagged handle to an in-order command stream (CUstream / hipStream / VkQueue). Kernel launches submitted to the same stream complete in submission order; launches on different streams run in parallel, subject to cross-stream synchronization via Event. The stream is also the borrow-scope anchor for launched buffers: a buffer passed as a kernel argument is borrowed for the lifetime of the launch, released at the next sync() ordered after it. (The spec’s KernelStream.default() is spelled current() here — default is a Cajeta keyword.)

KernelStream s #= KernelStream.current();   // per-thread default stream
KernelStream fresh #= KernelStream.create();
// enqueue async copies and launches on `fresh` ...
fresh.sync();      // block until everything submitted has completed
fresh.destroy();

Methods

Signature
static #KernelStream current() ⚑The per-thread default stream (handle 0); always the same stream for the same thread
static #KernelStream create() ⚑Create a fresh stream — a real backend stream object on CUDA/HIP, the default stream elsewhere; the caller owns it and must destroy() it
void sync()Block until every operation submitted to this stream — async copies and kernel launches — has completed; releases all deferred-borrow tokens
void waitFor(Event e)Insert a wait on e: future launches on this stream will not start until e has been recorded and signaled
void destroy()Destroy this stream’s backend object; no-op for the default stream, idempotent
void setDeferred(boolean on)Queue this stream’s launches and submit them together at the next point their results can be observed (see below)
void flush()Submit every launch this stream has queued, without waiting for them
int64 pendingLaunches()Launches queued on this stream and not yet submitted
static int64 deferStat(int32 which)Process-wide deferral counters by DEFER_* key: DEFER_QUEUED, DEFER_REPLAYED, DEFER_DIRECT, DEFER_GRAPHS_BUILT, DEFER_GRAPH_LAUNCHES, DEFER_PATCHES, DEFER_FLUSHES

Deferred launches

A launch is a driver call, and on the measured box (an RTX 4090 under WSL2) that call costs 6 to 13 microseconds whatever the kernel does. A decode token of an 8B model is 356 launches, so the floor alone is milliseconds a token, and the sequence of kernels is the same every token. setDeferred(true) makes the stream QUEUE its launches, arguments copied by value, and submit the queue at the next point where its results can be observed: a sync(), a host transfer, an event record, a buffer release, flush(), or turning deferral off. Nothing a program can observe changes; what changes is the cost of submitting.

A queue whose kernel sequence the stream has submitted before is replayed as one recorded submission with only the grids, blocks and arguments that changed patched in. On nvptx the recording is a CUDA graph: measured on the same box, 600 kernels replay in 0.6 ms against 5 to 6 ms submitted one by one, and a patch costs 0.2 microseconds. The first sight of a sequence goes launch by launch, the second records it. A backend without such a thing submits the queue launch by launch, and a synchronous backend (cpu) queues nothing: there the switch is accepted and has no effect.

A launch that cannot be queued (a texture, an image, a buffer array, a launch-time specialization) submits the stream’s queue ahead of itself and goes directly, so order on the stream is always the order of the launch calls. An armed device profiler keeps every launch direct, since it attributes each one as it is submitted; CAJETA_PROFILER_GPU=0 keeps host sampling with capture off.

One consequence to know: a queued launch has not started. Host work that used to overlap the device now precedes it, so defer where the launches are many and small, which is where the submit cost dominates.

CAJETA_XPU_DEFER=0 never queues (the A/B control); CAJETA_XPU_DEFER=all defers every stream from its first launch (the whole-suite validation lever). CAJETA_XPU_DRIVER_PROFILE=1 prints, at exit, how many times each driver entry that observes device state was called and how long the host spent in it, with the direct launches and the deferred submissions beside them; CAJETA_XPU_DRIVER_SITES=1 adds the cajeta source line of each call.

⚑ = @EntryPoint

See also

Source: docs/stdlib/xpu/KernelStream.md · 3 min read · 698 words