Architecture¶
These notes are the historical engineering record, kept for reference and for agents working in the codebase. The curated design pages live in the Design section.
Per-component design docs. Numbered roughly in build order. Source of truth for choices is ../decisions.md; when a doc disagrees, decisions wins.
Contents¶
| # | Doc | Status |
|---|---|---|
| 1 | 1-dataset.md: Dataset, BinMapper, readers |
done |
| 2 | 2-histogram.md: gradient/hessian sums, subtraction, parallel reduce |
done |
| 3 | 3-tree.md: Tree concept, DenseTree + ObliviousTree, depth-wise + oblivious growers, histogram splitter |
done |
| 4 | 4-objective.md: Objective concept, MSE, logloss |
done |
| 5 | 5-booster.md: Booster, training loop, update_one_iter |
done |
| 6 | 6-dispatch.md: registry, runtime → static boundary |
done |
| 7 | 7-parallel.md: parallelism seam, determinism contract |
done (decision 32) |
| 8 | 8-config.md: Config, TOML, CLI overrides |
done |
| 9 | 9-cli.md: subcommands, overrides, fit-time output |
done |
| 10 | 10-cuda.md: GPU histogram backend: builder policy, level batching, runtime capability |
done |
| 11 | 11-gpu-resident.md: the GPU device plane: resident buffers, kernels, precision, cross-library results |
done (decisions 40–42) |
| 12 | 12-grower-backend.md: grower data-plane: the LevelStep compile-time strategy |
landed (decision 41) |
| 13 | 13-device-residency.md: full device residency: gradients first, binning second |
refuted (decision 52) |
| 14 | 14-engine-narrative.md: one engine narrative: the level transaction |
executed (decision 53) |
| 15 | 15-device-binning.md: device binning: ingest joins the transaction narrative |
implemented (decision 54) |
| 16 | 16-compute-dag.md: the compute DAG: placement as a first-class design object |
framing (decision 54) |
| 17 | 17-categorical-splits.md: native categoricals: Fisher set splits + ordered-TS sketch |
declined by measurement (decision 58) |
| 18 | 18-manual-bin-edges.md: explicit bin edges in the model artifact |
shipped (decision 73) |
| 19 | 19-multi-gpu.md: single-node multi-GPU: a data-parallel backend beside the single-GPU one |
design, not built (issue #159) |
Cross-cutting concerns¶
Dispatch. Static poly inside Booster, runtime at config boundary. Flat table over cartesian_product_t<...>; one vcall at boundary, zero inside update_one_iter. See 6-dispatch.md + decision 26.
Threading. Shipped as a single seam, not a backend concept (decision 32): parallel::for_each_index in bonsai/parallel.hpp, OpenMP body with serial fallback, [parallel] n_threads config. Every parallel site assigns each index to exactly one thread with no cross-thread reductions. Details in 7-parallel.md.
Errors. Component constructors validate their config slice, throw ConfigError with key path. No central validator. CLI top-level catches.
Determinism contract (decisions 32/59/60): model bits are a pure function of the input, the config, and the configured thread count: the row-parallel fill's block plan scales with parallel.n_threads, so different N legitimately produce different (equally valid) bits, but a fixed N reproduces byte-for-byte on any machine and any architecture: arm64 and x86-64 train identical models (-ffp-contract=off, decision 59), gated per commit by the cross-arch CI workflow. The original any-thread-count claim (decision 32) predates the row-parallel fill; a silent serial fallback that once let builds differ is now a hard configure error (decision 60).
Precision. Float storage, double accumulators. Matches xgb/lgbm.
Doc conventions¶
- Open with status line pointing at ratifying
decisions.mdentries. - Code sketches show shape, not impl.
- Close with "What's not here" + cross-references.
- Filename:
N-<name>.md.
Test naming¶
Catch2 TEST_CASE names follow "<Component>: <behavior under condition>": PascalCase component, colon, behavioral phrase starting with a present-tense verb, condition trailing after when / if / for. One TEST_CASE per behavior; use SECTION for parameter variations within a behavior.
Tags stack a component tag with one or more behavioral tags drawn from a fixed set: [fit], [transform], [ctor], [edge], [nan], [perf], [smoke]. This makes --tags [nan] and --tags [fit] useful filters across files.
TEST_CASE("BinMapper: reserves a missing bin when column contains NaN",
"[bin_mapper][fit][nan]")
Stubs get a single "<Component>: smoke" case tagged [smoke], deleted once a real behavior test lands.