Skip to main content

Module backend

Module backend 

Source
Expand description

bb::Backend — Contract trait for tensor compute backends.

The Contract has THREE surfaces, exposed side-by-side:

  1. One typed method per mandatory primitive op (the 30 entries in bb_ir::tensor_primitives::TENSOR_PRIMITIVES_OPS): add, mul, matmul, reduce_sum, reshape, …. Components reach these inline through ctx.backends for short-form tensor math — an Index’s distance kernel calls backend.matmul(&query, &vectors)? instead of hand-rolling a loop.

  2. One method to execute a subgraph: execute(&GraphProto, HashMap<String, Tensor>, BackendAttrs) → HashMap<String, Tensor>. Backends that prefer whole-graph dispatch override this entry point; the per-op defaults call through to it via a one-node GraphProto.

  3. One dispatch entry point for BackendSubgraph carriers: dispatch(&GraphProto, inputs, attrs, completion) → ContractResponse. The engine calls this for every BackendSubgraph carrier op. The default falls through to execute synchronously. Backends with per-subgraph caching, JIT compilation, or async device execution override dispatch to return ContractResponse::Later while device work runs.

§How the two sides compose

Default impls in crate::contracts::backend_default_walk bridge the surfaces so a backend author overrides only the side that’s natural for their target:

  • CpuBackend overrides the 30 per-op methods directly (add runs ndarray’s Add impl, matmul runs dot, …). It does NOT override execute — the default walker uses the overridden per-op methods.

  • A Burn-style backend overrides execute natively (Burn compiles the whole GraphProto to its own IR + runs once). It does NOT override per-op methods — they default-wrap a one-node GraphProto and call execute.

Backends overriding neither side stack-overflow on the first call: every per-op default wraps into execute, whose default walks back to per-op, ad infinitum. Backends MUST override at least one side.

§Extension ops

Activation functions (Relu, Sigmoid, Softmax), pooling (MaxPool, AveragePool), normalization (BatchNormalization, LayerNorm), Conv, and so on are NOT on the Contract surface. They’re extensions — a backend MAY declare them via crate::roles::BackendRuntime::extension_opsets and handle them through its own execute override; OR a future lowering pass decomposes them into primitives so the Contract surface covers any graph.

Structs§

BackendAttrs
Per-call NodeProto context surfaced to Backend::execute so kernels overriding the whole-graph path see the original call site’s attributes + metadata alongside the body. Per-op methods (which receive their attributes positionally as typed args) don’t need this struct.

Traits§

Backend
User-facing Contract trait for a tensor compute backend.