Skip to content

Upgrade the Rust dependencies and fix the parse tests - #8

Open
ukaratay wants to merge 6 commits into
dkaratay/opt-experimentsfrom
dkaratay/v2-deps
Open

ukaratay wants to merge 6 commits into
dkaratay/opt-experimentsfrom
dkaratay/v2-deps

Conversation

@ukaratay

@ukaratay ukaratay commented Oct 4, 2026

Copy link
Copy Markdown
Collaborator

Every Rust dependency moves to its latest release that builds with Rust 1.97, and both packages now declare that minimum. The library's source does not change.

The JSON loader stays on simd-json, now 0.18.1. Before upgrading I measured serde_json 1.0.151 against it, through a copy of the loader ported to each parser, on the Expedia XGBoost export (float32, 53 MB) and the largest valid export, FLCHAIN horizon 1,024 LightGBM (float64, 66 MB). M4 Pro, 2026-10-04, median of 7:

simd-json 0.18 serde_json
Expedia: load, peak RSS 112 ms, 528 MB 153 ms, 502 MB
FLCHAIN: load, peak RSS 148 ms, 659 MB 199 ms, 563 MB

serde_json is within the 1.5× load time we set as the limit for switching, but with default features it misparses floats: 28,382 of FLCHAIN's 215,186 node values differ from Rust's correctly rounded parse. Its float_roundtrip feature fixes that. It also handles 4 of 29 malformed inputs differently: it accepts integers wider than 64 bits, rejects lone surrogates, and keeps the last of duplicate keys. simd-json 0.14 and 0.18 parse every value identically, and both node pools equal the binary checkpoints bit for bit.

  • rust-version = "1.97" is set once in [workspace.package] and inherited by both packages. Resolver 3 is explicit; it is the resolver that picks versions compatible with that minimum. cargo update held nothing back.
  • libloading 0.9 moved the dlerror text from the error's Display into its source(). The harness prints the source, so a missing symbol still reads "dlsym failed: dlsym(..., forest_root): symbol not found".

Tests:

  • parse_test and config_test defaulted to expedia/nt50_md8, a cell no prepare run produces. Two parse tests failed on every branch, and the other four returned without testing anything. parse_test now defaults to flchain/nt500_md8_h16, where all six run, and config_test to expedia/nt500_md8. The two reject tests need no artifacts and no longer write to /tmp.
  • The JSON tests, including test_binary_matches_json from Exact leaf sums and groups of any width #6, skip an export only when a byte scan finds Treelite's empty "threshold": , values, 22 of them in the Expedia LightGBM dump. Any other parse error fails, so a parser regression cannot hide behind the skip. 15 exports are compared and 1 is skipped.

Production predict() before and after, alternating builds over 5 rounds (M4 Pro, 2026-10-04), is within ±4% in 12 of 14 cells, from G = 16 to 1,008 plus Expedia. G = 256 is 11–12% slower in the default build, but its predict functions are instruction-for-instruction identical; only their addresses moved. Rebuilt with 64-byte function alignment, every cell is within ±3%, except FLCHAIN horizon 1,024 XGBoost, which is 6% faster. From this PR on, a slowdown counts as a regression only if it persists under fixed function alignment.

@ukaratay
ukaratay added this pull request to stack #7 October 4, 2026 18:19
ukaratay and others added 6 commits October 6, 2026 16:16
parse_test.rs and config_test.rs defaulted to expedia/nt50_md8, a cell no
local prepare run produces. Two parse tests failed on every branch because of
it: test_reject_non_json panicked on the missing walker_config.json instead of
the extension check, and test_reject_unknown_json_schema returned early from a
should_panic test. The other four returned before testing anything.

parse_test.rs now defaults to flchain/nt500_md8_h16, whose LightGBM JSON
export is valid, so all six tests run. config_test.rs asserts Expedia's
grouping and defaults to expedia/nt500_md8, whose walker_config.json has the
same 21 features, 10 varying features and 37-row maximum. The two reject
tests no longer need artifacts: they write a five-field configuration, and
the bad model that used to go to /tmp/bad_model.json, under
CARGO_TARGET_TMPDIR.

Treelite writes `"threshold": ,` for nonfinite values, as in the Expedia
LightGBM export (22 times). The loader rejects such dumps instead of
repairing them, as docs/treelite-loading.md documents. The four parse tests
that load a LightGBM JSON export and correctness.rs's
test_binary_matches_json skip a file only when its load fails with a JSON
syntax error and a byte scan, independent of the parser under test, finds
empty thresholds in it; they print the path, the count and the error. Any
other load error fails, so a parser regression on valid JSON cannot hide as
a skip, and every other JSON export, including Expedia's XGBoost one, is
still compared with its binary. Skips for missing models or predictions in
the correctness suite are printed too.

Co-authored-by: AI (Pi/Claude Opus 5.5) <noreply@pi.dev>
rust-toolchain.toml pins 1.97.1, but neither package declared a
rust-version, so Cargo could resolve dependencies that need a newer compiler
and crates.io would publish treewalker-gbdt without a minimum. The version is
set once in [workspace.package]; that table alone applies nothing, so both
packages inherit it with rust-version.workspace = true. Resolver 3, the
edition 2024 default, is set explicitly: it is the resolver that picks
dependency versions compatible with rust-version, which the dependency
upgrades that follow rely on.

Co-authored-by: AI (Pi/Claude Opus 5.5) <noreply@pi.dev>
simd-json 0.14 was four minor releases behind. Before upgrading, serde_json
1.0.151 was measured against simd-json 0.18.1 on the Expedia XGBoost export
(float32, 52,979,912 bytes) and the FLCHAIN horizon-1,024 LightGBM export
(float64, 65,694,653 bytes, the largest valid one), each through a copy of
the loader ported to the other parser. On the M4 Pro, median of 7:

- load time: serde_json 153 and 199 ms, simd-json 112 and 148 ms (1.35x);
- peak RSS: serde_json 502 and 563 MB, simd-json 528 and 659 MB;
- parsed values: by default serde_json misparses 2,258 of 84,692 thresholds
  and 8,938 of 85,192 leaves on Expedia and 28,382 of 215,186 FLCHAIN node
  values against Rust's correctly rounded parse. With float_roundtrip it
  matches, as do simd-json 0.14 and 0.18, and every node pool equals the
  binary checkpoint's bit for bit;
- errors: serde_json accepts integers beyond 64 bits as floats and rejects
  lone surrogates, which simd-json does the other way around, and keeps the
  last of duplicate keys where simd-json keeps the first. The 64 MiB size
  limit and the depth-64 check run before either parser.

serde_json is within 1.5x but does not match on values without a feature or
on error behavior, so the loader stays on simd-json. 0.18 needs no source
changes, parses every value identically to 0.14, and loads in the same
time.

Co-authored-by: AI (Pi/Claude Opus 5.5) <noreply@pi.dev>
libloading 0.9 moved the system's dlerror message from the error's Display
text into its source, so the harness's load messages would have said only
"dlopen failed" or "dlsym failed". A small report helper prints the source
after the error, as before: for example "lleaves: symbol error: dlsym failed:
dlsym(..., forest_root): symbol not found". Symbol names are now C string
literals, which 0.9 accepts without the null-byte check and copy that byte
strings need.

Co-authored-by: AI (Pi/Claude Opus 5.5) <noreply@pi.dev>
cargo update, resolved against rust-version 1.97, moves every other crate to
its latest compatible release: among the direct dependencies, libc 0.2.190,
serde 1.0.229 and rustc-hash 2.1.3; the rest are transitive, mostly through
ndarray-npy and the QuickScorer baseline. No crate is held back by the
minimum Rust version. ndarray 0.17 and ndarray-npy 0.10 are already the
latest minor releases.

Co-authored-by: AI (Pi/Claude Opus 5.5) <noreply@pi.dev>
cargo doc --no-deps warned that the crate docs link to CellData and
collect_methods, which are private, so the links could not resolve. They are
plain code spans now, and cargo doc builds both packages without warnings
under every feature combination.

Co-authored-by: AI (Pi/Claude Opus 5.5) <noreply@pi.dev>
@ukaratay ukaratay self-assigned this Oct 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants