plan design
Design goal
A plan states what data-parallel operation to perform, on what input shape,
under which policy, without saying how or running it. Keeping this as plain,
comparable data lets luna_thread validate work before it reaches a runtime,
embed it in workflows, and translate it for different backends. The package
must stay backend-independent and build on every target.
Mathematical background
Let the input be a sequence with . The four plan kinds denote these functions:
A reduction kernel must be associative, , so that the bracketing a parallel runtime chooses does not matter. The three kernels of v1 are commutative and associative on the integers:
| Kernel | Identity | |
|---|---|---|
Sum | ||
Min | none in ; the largest value of a fixed-width type | |
Max | none in ; the smallest value of a fixed-width type |
Design decisions
Plans are data, not closures
The plan records the kind, the element type, the length, the policy, an
optional named kernel and the ordering, and nothing executable. A closure
could express any and , but it cannot be compared, inspected,
validated, or passed to C threads. Named kernels can be checked against what a
runtime implements: reduction_kernel_is_supported_in_v1 accepts exactly the
kernels with a C implementation, and Custom(name) keeps a place for kernels
that a later runtime may register.
Ordering is a property of the kind
OrderingGuarantee says whether results must keep input positions. For a
reduction with a commutative and associative , the result is the same
for every permutation of the input:
by the generalised commutative law, so RelaxedOrder is safe for Reduce and
MapReduce, and for Map it only permits results to be produced out of order.
A scan is different: depends on which elements come before position
. For and the swap , the sums are
so a scan has no meaning without input order. scan therefore always sets
PreserveInputOrder, and validate reports both UnsupportedOrdering and
ScanRequiresStableOrdering for a hand-built relaxed scan.
The v1 subset is checked, not encoded in types
ValueType lists F32, F64, Bytes and Opaque, and ExecutionPolicy
can name the JavaScript backend and asynchronous mode, although v1 supports
none of them. The types describe the whole model of the specification;
validate decides what the current runtime accepts. When a runtime grows, the
predicates change and user code that builds plans does not.
Integer element types are also the ones for which chunked reduction is
well-defined. Floating-point addition is not associative: in Double,
so a parallel sum of F64 data would depend on the worker count and chunk
size. Supporting it needs a decision about which bracketing to promise, which
v1 does not make.
Validation reports every issue
validate returns an array of all issues rather than the first one, so a
caller can show every problem of a plan together, and a workflow can wrap each
one as InvalidComputePlan. The issues are independent tests, listed in a
fixed order. is_runnable is the conjunction of their negations:
Size checks stop short of the cover condition
validate rejects and because no runtime can use more chunks
than elements. It does not check the native cover condition
, which belongs to one backend’s chunking strategy
(see the native backend design). Backends check their own
conditions when they build requests or run kernels.
Correctness / invariants
- Builders fix the kind.
map,reduce,scanandmap_reduceproduce plans of the corresponding kind;reduceandmap_reducealways carry a kernel;scanandreducealways preserve order. - Validation is total and pure.
validateterminates for every plan, has no effects, and depends only on the plan’s fields. - Supported plans name implemented kernels. If
and reduces, its kernel is
Sum,MinorMaxand its element type isI32orI64, the combinations the C runtime implements. - Equality is structural. Two plans are equal exactly when all fields are equal, so plans can serve as keys and test expectations.
Alternatives rejected
- A generic
Plan[T]. Typing the element would push the element type into every workflow node and backend signature, and the C interface would still need a runtime tag. AValueTypetag keeps plans of different element types in one array. - Separate types per kind.
MapPlan,ReducePlanand so on would encode “reduce has a kernel” in the type, but workflows and requests would need a sum type over them anyway;PlanKindplus validation is that sum type. - Validation inside the builders. Builders that return
Resultwould make the hand-built plans that tests and tools need impossible to express.
Boundaries
The package does not execute plans, hold input data, allocate buffers, choose chunk sizes, check backend-specific conditions, or describe user-defined functions; the map function is implied by the backend (doubling in the native backend), not stored in the plan.