tiny.accy.tensor.session
Defined in tensor.
A way to compile tensor programs for the host CPU inside the calling process and run them directly on memory the caller supplies.
API (14)
Actions
Public operations.
Session.compile: A caller passes a program's wire bytes, the versioned byte encoding of a tensor program, to get a handle it can run.Session.deinitSession.initSession.inputDescriptorsSession.invoke: A caller runs a compiled program on its own input and output buffers.Session.outputDescriptorsSession.release: Frees the compiled program, its descriptors and its decoded form.Status.fromError
Types and contracts
Public types and contracts.
Descriptor: A caller reads these descriptors to size and type the buffers it passes to a run: the value gives the element type, axes, byte length and required alignment of one input or output.Error: A caller matches on these to handle session failures: the set lists every failure a session call can return.Handle: A caller keeps this handle to name one compiled program in later calls by slot index and generation count.Session: A host creates a session to compile tensor programs for the CPU backend inside this process and run them on memory it supplies.Status: A host sends these numbers to report failures across a language boundary: the codes run from 0 for success to 8 for a failed launch.
Values and defaults
Public values and defaults.
max_programs: A caller sizes its bookkeeping by this bound on programs held at once: one session holds at most 64 compiled programs at a time.
Source
Source: lib/accy/src/tensor/session/session.zig:69
zig
/// A caller reads these descriptors to size and type the buffers it passes to a run: the value/// gives the element type, axes, byte length and required alignment of one input or output. `dims`/// points into the compiled program's own types and stays valid until that program is released.pub const Descriptor = struct { dtype: tensor.DType, dims: []const tensor.Dim, byte_size: usize, alignment: u32,};Source: lib/accy/src/tensor/session/session.zig:20
zig
/// A caller matches on these to handle session failures: the set lists every failure a session call/// can return. Each failure maps to exactly one stable status number.pub const Error = error{ InvalidProgram, UnsupportedVersion, InvalidHandle, InvalidBuffer, TooManyPrograms, OutOfMemory, CompileFailed, LaunchFailed,};Source: lib/accy/src/tensor/session/session.zig:61
zig
/// A caller keeps this handle to name one compiled program in later calls by slot index and/// generation count. `Session.release` changes the slot's generation, so every copy of the old/// handle is refused with `error.InvalidHandle` from then on.pub const Handle = struct { index: u32, generation: u32,};Source: lib/accy/src/tensor/session/session.zig:89
zig
/// A host creates a session to compile tensor programs for the CPU backend inside this process and/// run them on memory it supplies. The session runs programs forward only, with no transform that/// computes derivatives.pub const Session = struct { allocator: std.mem.Allocator, cpu: *CpuState, entries: [max_programs]Entry = @splat(.{}), /// A caller reads this field to log why the last failing call failed: the field holds the inner /// error behind the most recent failure that went through the session's error mapping, and it /// is for diagnostics only. Failures returned directly, such as a full table, a stale handle, a /// buffer count mismatch or a failed allocation while describing buffers, leave it unchanged. last_cause: ?anyerror = null, pub fn init(allocator: std.mem.Allocator) Error!Session { const cpu = allocator.create(CpuState) catch return error.OutOfMemory; cpu.* = CpuState.init(allocator); return .{ .allocator = allocator, .cpu = cpu }; } pub fn deinit(self: *Session) void { for (&self.entries) |*entry| { if (entry.live) self.releaseEntry(entry); } self.cpu.deinit(); self.allocator.destroy(self.cpu); self.* = undefined; } /// A caller passes a program's wire bytes, the versioned byte encoding of a tensor program, to /// get a handle it can run. The call decodes the bytes, compiles the program for the CPU, and /// loads the result into a free slot. Bad bytes return `error.InvalidProgram` or /// `error.UnsupportedVersion`, a compile or load failure returns `error.CompileFailed` or /// `error.OutOfMemory`, and a full table returns `error.TooManyPrograms`. The caller may free /// `bytes` once the call returns, because decoding copies names and payloads into the program. pub fn compile(self: *Session, bytes: []const u8) Error!Handle { const index = self.freeIndex() orelse return error.TooManyPrograms; var program = tensor.wire.decode(self.allocator, bytes) catch |err| { return self.fail(decodeError(err), err); }; errdefer program.deinit(); const cpu = self.cpu.handle(); const options = accy.executable.FragmentCompilerOptions{ .artifact_format = .cpu_object }; const compiled = tensor.lower.compileFragment( self.allocator, cpu, &program, options, ) catch |err| return self.fail(compileError(err), err); const fragment = accy.executable.loadFragment( self.allocator, cpu, compiled, options, ) catch |err| return self.fail(compileError(err), err); errdefer fragment.deinit(); const capabilities = cpu.queryCapabilities() catch |err| { return self.fail(compileError(err), err); }; const min_alignment = capabilities.memory.min_buffer_alignment; const artifact_plan = compiled.artifactPlan(); const descriptors = try describe(self.allocator, &program, artifact_plan, min_alignment); std.debug.assert(descriptors.len == program.parameters.len + program.outputs.len); const entry = &self.entries[index]; std.debug.assert(!entry.live); entry.* = .{ .live = true, .generation = entry.generation, .program = program, .compiled = compiled, .fragment = fragment, .descriptors = descriptors, .input_count = program.parameters.len, }; return .{ .index = index, .generation = entry.generation }; } pub fn inputDescriptors(self: *const Session, handle: Handle) Error![]const Descriptor { const entry = &self.entries[try self.indexOf(handle)]; return entry.descriptors[0..entry.input_count]; } pub fn outputDescriptors(self: *const Session, handle: Handle) Error![]const Descriptor { const entry = &self.entries[try self.indexOf(handle)]; return entry.descriptors[entry.input_count..]; } /// A caller runs a compiled program on its own input and output buffers. The input and output /// slices follow the program's descriptors in order, and a count that does not match returns /// `error.InvalidBuffer`. The run uses the caller's buffers in place wherever the plans allow, /// and copies any input the program writes so the caller's inputs stay unchanged. Each output /// must share no bytes with any input or with another output, and a buffer that breaks this or /// the descriptor's size or alignment returns `error.InvalidBuffer`. `scratch` backs the /// short-lived state of one launch, and a launch failure returns `error.LaunchFailed`. pub fn invoke( self: *Session, handle: Handle, scratch: std.mem.Allocator, input_bytes: []const []const u8, output_bytes: []const []u8, ) Error!void { const entry = &self.entries[try self.indexOf(handle)]; std.debug.assert(entry.live); const output_count = entry.descriptors.len - entry.input_count; if (input_bytes.len != entry.input_count) return error.InvalidBuffer; if (output_bytes.len != output_count) return error.InvalidBuffer; const bindings = binding.prepareBorrowed( self.allocator, self.cpu.handle(), entry.compiled.artifactPlan(), entry.compiled.launchPlan(), input_bytes, output_bytes, ) catch |err| return self.fail(bindError(err), err); defer bindings.deinit(); entry.fragment.submitInvocationWithOptions(scratch, bindings, .{}) catch |err| { return self.fail(launchError(err), err); }; entry.fragment.completeInvocationWithOptions(.{}) catch |err| { return self.fail(launchError(err), err); }; binding.completeBorrowed(bindings, output_bytes) catch |err| { return self.fail(launchError(err), err); }; } /// Frees the compiled program, its descriptors and its decoded form. After the call the handle /// and every descriptor slice from that program are invalid, and the handle is refused with /// `error.InvalidHandle`. pub fn release(self: *Session, handle: Handle) Error!void { const entry = &self.entries[try self.indexOf(handle)]; self.releaseEntry(entry); } fn releaseEntry(self: *Session, entry: *Entry) void { std.debug.assert(entry.live); entry.fragment.deinit(); self.allocator.free(entry.descriptors); entry.program.deinit(); entry.* = .{ .generation = entry.generation +% 1 }; } fn indexOf(self: *const Session, handle: Handle) Error!u32 { if (handle.index >= max_programs) return error.InvalidHandle; const entry = &self.entries[handle.index]; if (!entry.live or entry.generation != handle.generation) return error.InvalidHandle; return handle.index; } fn freeIndex(self: *const Session) ?u32 { for (&self.entries, 0..) |*entry, index| { if (!entry.live) return @intCast(index); } return null; } fn fail(self: *Session, public: Error, cause: anyerror) Error { self.last_cause = cause; return public; }};Source: lib/accy/src/tensor/session/session.zig:33
zig
/// A host sends these numbers to report failures across a language boundary: the codes run from 0/// for success to 8 for a failed launch. Once a number is assigned it keeps its meaning for good.pub const Status = enum(u32) { ok = 0, invalid_program = 1, unsupported_version = 2, invalid_handle = 3, invalid_buffer = 4, too_many_programs = 5, out_of_memory = 6, compile_failed = 7, launch_failed = 8, pub fn fromError(err: Error) Status { return switch (err) { error.InvalidProgram => .invalid_program, error.UnsupportedVersion => .unsupported_version, error.InvalidHandle => .invalid_handle, error.InvalidBuffer => .invalid_buffer, error.TooManyPrograms => .too_many_programs, error.OutOfMemory => .out_of_memory, error.CompileFailed => .compile_failed, error.LaunchFailed => .launch_failed, }; }};Source: lib/accy/src/tensor/session/session.zig:16
zig
/// A caller sizes its bookkeeping by this bound on programs held at once: one session holds at most/// 64 compiled programs at a time. Compiling another program while all 64 are held returns/// `error.TooManyPrograms`.pub const max_programs: u32 = 64;Source: lib/accy/src/tensor/root.zig:18
zig
pub const session = @import("session/root.zig");Source: lib/accy/src/tensor/session/root.zig
zig
//! A way to compile tensor programs for the host CPU inside the calling process and run them//! directly on memory the caller supplies. A host program, possibly written in another language,//! needs to compile and run array programs through a few calls and report failures across a//! language boundary as plain numbers. A host that hands over large buffers wants them used in//! place on every run. Host code can keep a program's name after it released the program, and a//! reused slot would then run the wrong program.//!//! A table inside the host process (*session*) holds up to 64 compiled programs, which arrive as//! versioned bytes and run on the CPU backend. Each compiled program is named by its slot and a//! generation count that changes on release (*handle*), so a name kept past release is refused.//! Runs use the caller's buffers in place wherever the plans allow, and inputs the program writes//! are copied first so the caller's inputs stay unchanged. Every failure maps to one fixed number//! that never changes meaning (*status code*).//!//! - *descriptor*: the element type, axes, byte length and alignment of one program input or//! output.const session = @import("session.zig");pub const Session = session.Session;pub const Handle = session.Handle;pub const Descriptor = session.Descriptor;pub const Error = session.Error;pub const Status = session.Status;pub const max_programs = session.max_programs;Complete call list for tensor.session.Session.compile
8 direct calls.
tiny.accy.tensor.lower.compileFragment[function] atlib/accy/src/tensor/lower.zig:271lib.accy.src.tensor.session.session.Session.fail[method] — private source atlib/accy/src/tensor/session/session.zig:242in nearest public ownerlib.accy.src.tensor.session.sessionlib.accy.src.tensor.session.session.Session.freeIndex[method] — private source atlib/accy/src/tensor/session/session.zig:235in nearest public ownerlib.accy.src.tensor.session.sessionlib.accy.src.tensor.session.session.compileError[function] — private source atlib/accy/src/tensor/session/session.zig:301in nearest public ownerlib.accy.src.tensor.session.sessionlib.accy.src.tensor.session.session.decodeError[function] — private source atlib/accy/src/tensor/session/session.zig:293in nearest public ownerlib.accy.src.tensor.session.sessionlib.accy.src.tensor.session.session.describe[function] — private source atlib/accy/src/tensor/session/session.zig:248in nearest public ownerlib.accy.src.tensor.session.sessionlib.accy.src.tensor.wire.decode[module] — private; no exact target atlib/accy/src/tensor/wire/decode.ziglib.gpu.src.cpu.queryCapabilities[function] — private source atlib/gpu/src/cpu.zig:127in nearest public ownerlib.gpu.src.cpu
Audit
| Definitions | 15 |
|---|---|
| Public names | 15 |
| Members | 27 |
| Version | 26.7.0 |
| Revision | daab053ee433 |