C · Arenas

Arenas

#include <uform/types.h> 4.0.0-rc.1 · 3ee5e534

Callers lend memory, never a way to allocate it, and every byte has one owner that picks where it lives once:

  • Weights and their packed copies belong to the model, placed by its load’s residency.
  • Weights, gradients and optimizer moments under training belong to the trainer, on its executor’s devices.
  • Activations, scratch and staging belong to the executor, grown to a high-water mark.

Every output whose length scales with the call lands in an arena, while fixed-size reports such as stats and descriptions stay caller fields. uform_arena_init places an arena where its executor computes, host pages or the memory of its GPU, and allocates nothing until a call writes into it. A call rewinds the arena it is given, invalidating the previous call’s outputs, and grows it only when the new outputs outsize everything it held, so a steady loop allocates once.

  • A NULL arena, or inputs inside the arena the call rewinds, report uform_status_invalid_argument_k.
  • An arena serves one call at a time, and a second call naming it finishes uform_status_contended_k.
  • A call on another residency or device than the arena’s reports uform_status_device_k.
  • A GPU arena grows on the first stream of the executor that made it, so it serves only that executor and is freed before it.
uform_arena_t arena = NULL;
uform_arena_init(&(uform_arena_init_t){.arena = &arena, .executor = executor, .outcome = &outcome});
for (size_t batch = 0; batch != batches_count; ++batch) {
    encode.texts = texts[batch], encode.arena = arena, encode.embeddings = &embeddings;
    uform_text_encode(&encode);
    if (encode.outcome->status != uform_status_success_k) break;
    consume(embeddings, encode.count); // valid until the next call given `arena`
}
uform_arena_free(arena);

§ Rows and Tapes

A verb’s count is its batch axis and nothing else. Outputs are views the call writes packed into its arena, so none carries a capacity for the caller to pick. uform_rows_view_t holds one row per item as data, stride, columns and scalar, field for field USearch’s rows, so pooled embeddings feed an index as they are. Ragged inputs are uform_tape_view_t tapes laid out as Apache Arrow lays them: elements end to end in data, and count + 1 offsets in host memory around them. Ragged outputs, such as per-token states, are uform_rows_tape_view_t: packed rows and one more offset than items. On a VRAM executor every output is device memory, which the caller copies out itself or hands to a USearch VRAM index as it is.

API

Types

uform_arena_tMemory the library allocates for the outputs only a call can size, reused across calls.
types.h:811 ↗
typedef struct uform_arena_handle_t * uform_arena_t;

Memory

Made by uform_arena_init where its executor computes: host pages on its memory domains, or the memory of its GPU, and a GPU that runs out reports uform_status_out_of_memory_k rather than falling back to the host. Each call given the arena rewinds it, which invalidates every output of the previous call, and refills it, growing only when the new outputs outsize everything it held, so a steady loop allocates once. To keep one call's outputs alive past the next call, pass the next call another arena.

  • A NULL arena is refused with uform_status_invalid_argument_k.
  • A call whose inputs point into the arena it is about to rewind reports uform_status_invalid_argument_k.
  • An arena serves one call at a time; a second call naming it finishes uform_status_contended_k, and a deferred one does so at submission.
  • A call on another residency or device than the arena's, or a call naming a GPU arena with another executor than the one that made it, NULL included, reports uform_status_device_k.
  • A blocking call borrows the arena until it returns, and a deferred one holds it until final.
  • A call that fails or is cancelled leaves its outputs NULL.

Notes

A GPU arena grows, fills and frees on the first stream of the executor that made it, so it serves only calls on that executor, and must be freed before that executor is.

uform_arena_description_tWhere an arena's outputs live.
types.h:860 ↗
struct uform_arena_description_t

Fields

residencyuform_residency_tRAM for host pages, VRAM for one device's memory.
deviceint32_tThe UForm ordinal of the device, as uform_gpu_devices lists it, or -1 for host pages.

Functions

uform_arena_initCreates an empty arena where executor computes, so its placement is decided once and GPU code keeps its outputs on the device without asking.
types.h:835 ↗
void uform_arena_init(uform_arena_init_t *arguments);

Nothing is allocated until a call writes into it. An arena of host pages may outlive the executor that made it, keeping its own memory until it is freed; a GPU arena keeps the rule uform_arena_t notes.

Parameters

arenauform_arena_t *Output for the new arena.
executoruform_executor_tThe executor whose calls the arena serves, which decides where it lives; NULL serves calls on the calling thread from host pages.
outcomeuform_outcome_t *Output for the outcome, final once its status leaves uform_status_deferred_k.
uform_arena_freeFrees the arena and every output in it; NULL is safe, and it cannot fail, so takes no outcome.
types.h:844 ↗
void uform_arena_free(uform_arena_t arena);

Waits until every deferred call naming the handle is final.

Parameters

arenainoutThe arena to be freed; invalid afterwards.
uform_arena_describeReports where an arena's outputs live, as the executor that made it decided, so a caller reads them as host memory or hands them to device code without remembering that executor.
types.h:889 ↗
void uform_arena_describe(uform_arena_describe_t *arguments);

Parameters

arenauform_arena_tThe arena to describe.
outcomeuform_outcome_t *Output for the outcome, final once its status leaves uform_status_deferred_k.
descriptionuform_arena_description_t *Output for where the arena's outputs live.

Statuses