HT Operator graphsSource ↗

COMPILER + PARALLEL RUNTIME

Parallel execution,
derived from source.

HT compiles operator calls and memory effects into a small symbolic program. At runtime, it resolves dependencies using actual addresses and schedules work within a fixed CPU and graph-state budget.

Explore execution ↓

01 / THE WORKLOAD

A matrix factorization with unevenly available work

Cholesky factorization takes a symmetric positive definite matrix A and finds a lower-triangular matrix L such that A = LLᵀ. The blocked algorithm partitions a matrix of width n into tiles of width b.

Each diagonal step factors one tile, solves the panel below it, then updates the trailing matrix. Independent tile operations can overlap. Operations that read or overwrite an earlier result must wait.

HT preserves those constraints while changing how work is represented and scheduled. It does not replace the numerical algorithms inside the operators.

C

Diagonal factor

cholesky factors the current diagonal tile.

T

Panel solve

trsm uses that diagonal to solve a tile below it.

M

Trailing update

madd updates a tile using two solved panel tiles.

From loop bounds to call counts

Let m = n/b. With r tile rows remaining, one step issues 1 diagonal call, r panel calls and r(r+1)/2 updates. Sum over r = 0 … m−1:

N = m + m(m−1)/2 + m(m−1)(m+1)/6
= m(m+1)(m+2)/6

Grouping the innermost update loop leaves m diagonal tasks, m(m−1)/2 panel tasks and the same number of update groups: m² scheduled tasks.

These are exact counts for this source traversal. The compiler derives loops from source; the runtime does not contain a Cholesky count table.

At fixed n, halving b roughly multiplies call count by eight while dividing arithmetic per tile update by eight. Total leading-order arithmetic stays proportional to n³, but arguments, footprints, queues and dependency bookkeeping must be processed far more often. Grouping amortizes those costs at the price of less scheduling freedom.

02 / EXECUTION LAB

Watch dependencies become worker tasks

ILLUSTRATIVE SIMULATION

Compare a fully materialized graph with a limited live frontier. A group runs its update calls sequentially in one callback. The graph and timelines below show the same numerical work under each policy.

The starting example emphasizes registration overhead. HT-style selection chooses grouped streaming here and matches the explicit grouped policy. Reduce registration cost or change the graph and worker count to explore other tradeoffs.

DiagonalPanelUpdate / update groupFaded: not registered · Outline: waiting · Filled: running · Muted: complete

Dependency graph

Columns follow diagonal steps. Select a node or timeline bar to inspect its prerequisites. Arrows show read/write ordering at tile granularity.

Select a task to inspect its dependencies.

Worker timelines

Horizontal gaps are idle time. Bar widths represent illustrative computation cost. The vertical marker follows the simulation clock.

What this simulation models, and what it leaves out

Tile-level RAW, WAR and WAW dependencies, source-order registration, active-window backpressure and completion-driven release are explicit. Diagonal, panel and update calls cost 1, 2 and 3 time units. A group costs the sum of its calls. Registration costs the amount selected above per scheduled task, on a producer that can overlap worker execution.

The full graph pays all registration cost before execution and uses downstream task depth for priority. Streaming uses source-order ready selection. The demo’s automatic policy uses a 100-task cap to make the transition visible on a small graph; real HT uses 32K–64K tasks plus footprint, edge and argument guards. Factor three selects grouping above m > 3×workers.

These are teaching-model timings, not predicted Kunpeng performance. Cache locality, memory bandwidth, kernel-specific timing, caller participation, worker continuation, graph-reduction details and allocation costs are not simulated. Grouping changes task count, not the amount of arithmetic.

Compare all policies at the current settings
PolicyTasksPeak liveTime unitsWorker utilization

03 / IMPLEMENTATION

Where the overhead goes, and how HT reduces it

Compiler analysis

LLVM extracts typed loop bounds, pointer expressions and operator calls. Automatic body inference inspects a separate copy of each available operator implementation, including supported helpers. It proves caller-visible effects and distinguishes private scratch from shared memory.

ABI checks, pointer provenance and execution-context checks reject unsupported calls. Source scalar helpers have a separate proof. The analysis bodies never enter executable numerical kernels. Opaque operators can use explicit trusted effect contracts.

Runtime binding

The runtime reads and fingerprints the required metadata, then binds current dimensions and addresses. Dependencies follow actual bytes: different formal pointer arguments may alias. Read-after-write, write-after-read and write-after-write hazards all constrain execution.

A decoded-model cache saves repeat decoding. It still validates the complete file on every entry and stores neither an input-specific ready graph nor a matrix result.

MechanismImplementationTradeoff / limit
Source preflightCount supported tasks and arguments before graph allocation. Stop once enough is known to reject an oversized graph.Unknown or saturated counts cannot justify exact reservation.
Packed graph storageReserve known node/argument sizes after the first task passes guards. Store arguments and successor lists contiguously.Fine graphs still cost construction time and memory.
Transitive edge reductionIf W→R→V already orders two writers, omit redundant W→V after merging actual-address accesses.Reachability must survive aliasing. Fewer edges do not guarantee lower wall time.
Priorities and continuationPrefer deeper downstream paths. Continue a newly ready successor locally and publish other ready work.Unweighted depth does not estimate numerical kernel cost.
Bounded source-loop groupsCompose a group’s effects from the fine model, then stream native loop callbacks through the live window.Long groups can hide ready calls and create uneven worker loads.
Worker and shape reuseReuse thread teams and pointer-independent affine geometry. Bind current addresses separately.Initial setup is timed. Reuse does not establish pointer disjointness.
Scalar stores and copyingEligible fills avoid per-element queue traffic; the compact build enables a parallel-copy path.Eligibility guards and source ordering remain necessary.

Bounded is a storage policy. Grouped is a granularity choice.

A full graph exposes global lookahead but stores all tasks and dependencies. A bounded stream registers work in source order, waits when the live window fills, then releases capacity as tasks retire. “Ungrouped” does not mean “unbounded.” The older factor-three dispatcher selected between two compiled variants; it was not inherently a full-graph scheduler.

Current compact HT tries a fine graph under a checked task cap of min(65536, max(32768, 4096 × min(workers, affinity CPUs))). Other resource and geometry guards also apply. It then tries source-loop composition and finally bounded fine execution. Rejected graph attempts run no callbacks.

For n=2304, b=8, HT executes 4,022,880 numerical calls through 82,944 scheduled tasks: 288 diagonal calls, 41,328 panel calls and 41,328 update-loop groups. With 23 requested workers, the default native streaming window permits 92 live tasks. Neither the full call list nor all grouped tasks need to reside in the scheduler at once.

13 bytes describes a program

The current factorization model expands from 13 bytes to its complete 361-byte canonical form. Structural coding removes impossible choices and uses variable-length fields. Larger programs use larger files or general encodings. Thirteen bytes is an observed size, not a format limit.

Limits are part of the interface

Supported typed loops and memory effects are explicit. Unknown contexts, unsafe helpers and unsupported ABIs reject. Task/window bounds do not bound total RSS: matrices and exact-footprint fallback also consume memory. Matrices execute sequentially.

Read the code-linked implementation walkthrough ↗

04 / MEASURED ON KUNPENG 920

Current implementation, measured end to end

Seven fresh-process repetitions on 24 physical CPUs, with strict memory binding to node 0. HT uses 23 background workers plus caller participation where supported. Serial and HT execute identical external numerical kernels. All outputs pass numerical verification and match serial bit for bit.

The 20-matrix batch and one-case large-input runs are separate campaigns. Timings include output allocation/copying, worker initialization, metadata loading, dependencies, scheduling, kernels and matrix writes. Input/output file operations and verification are outside compute timing. Process launch and dynamic loading appear in runner wall time.

All 20 matrix cases
CasenbSerial msHT msSerial / HT

Separate per-case medians need not sum to the median batch total. Small cases can be slower under parallel execution because setup outweighs useful parallel work.

Large matrices with small tiles

nbSeedMedian sRange sMedian peak RSS MiB

No serial fallback occurred in these HT measurements. These results describe this implementation and shared-host environment, not a guarantee for every input or machine.

LLVM 22.1.8, GCC 10.3.1 headers. Execution source f56754c, test snapshot 9a8c789; later changes are documentation and publication cleanup. Full reference-enabled suites pass 66/66 on x86-64 and AArch64, with separate selected sanitizer checks.

Download current measurement summary (JSON) ↓