Kernels and Compute¶
This page explains how Diffract kernels work: configuration, dependencies, aggregation, and parallel execution.
Fields vs kernels¶
A field is a named value stored on each parameter (e.g.,
frob_norm,stable_rank).A kernel is a function that produces one or more fields.
When you call:
session.compute.apply("stable_rank")
Diffract finds the kernel that produces stable_rank and executes it (plus any
dependencies).
Run session.compute.list_available_kernels(verbose=True) to list every registered
kernel with its required and produced fields.
Configuring kernels¶
Some kernels accept configuration parameters. Use session.compute.configure_kernel()
before computing:
from diffract import Session
session = Session(profile="ram")
with session:
session.compute.configure_kernel("hard_rank", rtol=1e-6)
session.compute.apply("hard_rank")
Kernel configuration is process-global: the registry is a module-level singleton shared by all sessions in the process. Configuration set in one session applies to every other session and resets only when the interpreter restarts.
Dependency resolution¶
Each kernel declares its required input fields and output fields. Diffract builds a dependency graph and executes kernels in topological order.
Example:
Kernel A:
produce_fields=("x",)Kernel B:
require_fields=("x",),produce_fields=("y",)
Computing y automatically executes A first, then B.
Kernel signatures¶
Diffract infers kernel metadata from the Python function signature:
Parameters without defaults → required input fields
Parameters with defaults → configurable keyword arguments
def hard_rank(esd, *, rtol: float = 1e-5) -> int: ...
Here, esd is a required field; rtol is configurable via
session.compute.configure_kernel("hard_rank", rtol=...).
Note: *args and **kwargs are not allowed in kernel signatures.
Apply levels¶
Kernels operate at one of three levels:
Level |
Scope |
Example |
|---|---|---|
|
Per parameter |
|
|
Per model (aggregates by |
|
|
Per parameter name across models |
|
Contextual field names¶
Aggregated kernels write results with a deterministic suffix to avoid collisions:
some_metric@models[m1,m2]@params[layer.0.weight]
When you call results.export_aggregates("some_metric", ...), Diffract matches both the
base name and contextual variants.
Kernel outputs¶
Diffract normalizes kernel return values:
Return type |
Behavior |
|---|---|
|
Used as-is: |
|
Mapped positionally to |
scalar |
Stored under the single declared field |
This allows one kernel to produce multiple fields in a single pass.
Execution flow¶
For per-parameter kernels:
Filter to parameters missing the target fields
Group the pending parameters into chunks sized by an approximate read budget, derived from the cache manager’s available bytes with headroom
For each chunk: prefetch the required input fields into memory, then execute the kernel on every parameter in the chunk
Chunk sizes are estimated from stored field metadata (shape and dtype), so large fields such as weight matrices produce small chunks and scalar fields produce large ones.
Worker pool configuration:
[parallel.thread_pool]
max_workers = 8
[parallel.process_pool]
max_workers = 8
Parallelism¶
Parallelism is controlled by:
parallel.thread_pool.max_workers— threads for view filtering and field prefetchingparallel.process_pool.max_workers— worker processes for kernel executionEach kernel’s
KernelExecutionProtocol(SEQUENTIALorPARALLEL)
If a kernel is marked PARALLEL, its tasks run on a ProcessPoolExecutor sized by
parallel.process_pool.max_workers.
Practical notes:
Kernel implementations must be picklable (safe for multiprocessing)
Prefer top-level functions; avoid closures with non-serializable state
Parallelism has overhead — it helps when per-task computation is substantial
Registering custom kernels¶
Built-in kernels live in diffract/core/compute/kernels/. Register your own with the
@session.compute.kernel() decorator — required input fields and configurable keyword
arguments are inferred from the function signature:
import torch
from diffract import Session
model = torch.nn.Sequential(torch.nn.Linear(32, 64))
session = Session(profile="ram")
with session:
@session.compute.kernel()
def scaled_frob_norm(frob_norm: float, *, scale: float = 1.0) -> float:
return frob_norm * scale
session.models.add(model, model_id="m1")
session.compute.configure_kernel("scaled_frob_norm", scale=2.0)
session.compute.apply("scaled_frob_norm")
metrics = session.results.export_metrics("scaled_frob_norm", export_format="dict")
By default the kernel is named after the function, produces a single field with the same
name, runs at PARAMETER level, and executes sequentially. The decorator accepts
name, require_fields, produce_fields, apply_level, execution_protocol, and
restrictions for full control. For example, an IN_MODEL kernel receives each
required field as a tuple of per-parameter values and writes one aggregate per model:
import numpy as np
from diffract.core.compute.execution import KernelApplyLevel, KernelExecutionProtocol
with session:
@session.compute.kernel(
name="total_frob_norm",
apply_level=KernelApplyLevel.IN_MODEL,
execution_protocol=KernelExecutionProtocol.SEQUENTIAL,
)
def total_frob_norm(frob_norm: tuple[float, ...]) -> float:
return float(np.sum(np.square(frob_norm)))
session.compute.apply("total_frob_norm")
aggregates = session.results.export_aggregates(
"total_frob_norm", export_format="dict"
)
Because the registry is process-global, a kernel registered through one session is
available to every session in the process. Kernels marked PARALLEL must be picklable
top-level functions (see Parallelism).
Built-in fields reference¶
Every built-in field – its formula, apply level, required inputs, and configuration – is documented in the metric catalog, generated from the kernel registry so it never drifts from the registered kernels. The per-category pages there give the mathematics, conventions, and pitfalls.
Fit method¶
The fit kernels – power_law_fit, truncated_power_law_fit, and exponential_fit –
accept a fit_method keyword argument selecting the fitting implementation:
"auto"(default) – the accelerated taichi implementation when thetaichiextra is installed and the ESD has at least 100 points; thepowerlawlibrary otherwise. If taichi is installed but fails to initialize,autofalls back to thepowerlawlibrary."powerlaw"– always thepowerlawlibrary."diffract"– always the accelerated implementation; raises if taichi is unavailable or fails to initialize.
The 100-point floor is statistical, not performance: the accelerated fitter only
considers xmin candidates that leave a tail of at least 50 points (the reliability
bound of Clauset et al.), so below roughly twice that it cannot select a tail at all. On
small ESDs (roughly 100-300 points) this constrained search yields more conservative
estimates than the powerlaw library’s unrestricted one, which minimizes KS over
arbitrarily small tails; pass fit_method="powerlaw" for exact parity with it. For the
same reason, the p-value kernels return NaN when the fitted tail has fewer than 50
points.
Select the implementation per kernel:
session.compute.configure_kernel("power_law_fit", fit_method="powerlaw")
session.compute.apply("pl_alpha")