Skip to content

rust: return the dictionary parameters that training selected - #378

Open
pylaterreur wants to merge 1 commit into
indygreg:mainfrom
pylaterreur:rust-train-dictionary-params
Open

pylaterreur wants to merge 1 commit into
indygreg:mainfrom
pylaterreur:rust-train-dictionary-params

Conversation

@pylaterreur

Copy link
Copy Markdown

In the Rust backend, train_dictionary() returns the k and d it was called with, not the ones ZDICT_optimizeTrainFromBuffer_fastCover() selected. With the default k=0, ZstdCompressionDict.k is 0, and d is 0 when it is searched. The dictionary data itself is correct. This is the test_optimize failure (AssertionError: 0 not found in (50, 2000)) in every "Test Rust Backend" step of test.yml, e.g. all 41 Rust steps of the 2026-11-06 scheduled run. The step has continue-on-error: true, so the jobs stay green.

zstd writes the selected parameters back into the params struct, but the struct was passed through a shared reference cast to *mut, so the compiler treats it as unchanged by the call. The release build stores the original k/d in the result (the call passes %params as readonly in the LLVM IR); a debug build of main returns the right values. This passes a mutable reference instead, as the C backend passes &params. test_optimize passes d=6, so test_optimize_d also checks a searched d.

Rust backend before/after on Linux x86_64 with GitHub's CPython builds and test.yml's settings (uv 0.8.11, ZSTD_SLOW_TESTS=1, ZSTD_WARNINGS_AS_ERRORS=1, hypothesis ci profile, PYTHONDEVMODE=1, -n 2):

main this PR
3.12.16 1 failed, 291 passed 293 passed
3.13.18 1 failed, 291 passed 293 passed
3.14.10 1 failed, 291 passed 293 passed
3.15.2 1 failed, 291 passed 293 passed
3.15.0t 1 failed, 291 passed 293 passed

On my fork, with the Rust step's continue-on-error removed so that a failure would fail the job, test.yml passes in all 48 jobs, and all 41 Rust steps report 293 passed. I left continue-on-error alone here.

Prepared with the help of Claude Code.

🤖 Generated with Claude Code

https://claude.ai/code/session_01GwUvhmzgfkiZ6vo4uGyesN

train_dictionary() passes its ZDICT_fastCover_params_t to
ZDICT_optimizeTrainFromBuffer_fastCover(), which writes the parameters
it selected back into the struct. But the struct was passed through a
shared reference cast to *mut. Writing through such a pointer is
undefined behavior: the compiler marks the pointer readonly, and release
builds return the k and d values that were passed in. With the default
k=0, ZstdCompressionDict.k was 0, and d was 0 whenever d was searched.
test_optimize has been failing this way in every "Test Rust Backend" CI
step ("AssertionError: 0 not found in (50, 2000)").

Pass a mutable reference instead, as the C backend passes &params, so k
and d match what the C and CFFI backends return. test_optimize passes
d=6, so add test_optimize_d to also check a searched d.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01GwUvhmzgfkiZ6vo4uGyesN
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.

1 participant