Skip to main content
The examples/simple directory contains two minimal programs that each multiply two matrices using ggml. They demonstrate the two main approaches to memory and compute management:

simple-ctx

Context-based allocation. All tensors and the compute graph live in a single ggml_context. Simple to use; CPU-only.

simple-backend

Backend-based allocation. Separates graph definition from execution. Supports CPU, CUDA, Metal, and other backends.
Both programs compute A × Bᵀ for two matrices and print the result.

Context-based approach (simple-ctx.cpp)

This is the legacy API. Memory for tensors and the compute graph is allocated inside a single ggml_context using a fixed-size memory pool.
1

Calculate and allocate the memory pool

Before creating any tensors you must calculate the total memory needed and pass it to ggml_init:
Setting no_alloc = false means tensor data buffers are allocated immediately inside the memory pool.
2

Create tensors and copy data

Allocate 2D tensors and copy the input matrices into them:
Note the argument order for ggml_new_tensor_2d: dimensions are (cols, rows) because ggml uses column-major storage.
3

Build the compute graph

Describe the computation by connecting tensors with operations. ggml_mul_mat(a, b) computes A × Bᵀ:
ggml_build_forward_expand walks the tensor dependency tree and records all nodes needed to produce result.
4

Run the computation

Execute the graph on the CPU:
5

Read the result and free memory

Copy output data out of the tensor buffer, then free the context:

Full source

Backend-based approach (simple-backend.cpp)

The backend API separates graph definition from execution and works with any ggml backend — CPU, CUDA, Metal, and others. The key difference is that tensor data is allocated by the backend scheduler after the graph is built, not inside a context.
1

Initialize backends

Load all available backends and create a scheduler that picks the best device:
The scheduler runs each graph node on the highest-priority backend that supports the operation, falling back to CPU for unsupported ops.
2

Build the compute graph with no_alloc = true

Create a temporary context only to define the graph structure. Set no_alloc = true because the scheduler will allocate tensor data later:
3

Allocate and upload tensor data

Let the scheduler allocate backend memory, then upload the input data:
4

Run the computation

Execute the graph through the scheduler:
5

Download the result and clean up

Copy output data back to CPU memory, then free all backend resources:

Full source

Choosing an approach

For new projects targeting hardware acceleration, prefer the backend-based API. It is more verbose but works transparently across all ggml-supported devices.