Fix in-place SetPrune aliasing#12202
Merged
RAMitchell merged 3 commits intoMay 11, 2026
Merged
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
This PR fixes an in-place aliasing bug in xgboost::common::WQSummary::SetPrune where writing pruned entries back into the same buffer could overwrite source entries that were still needed later in the prune loop, potentially producing duplicate (non-increasing) values in the pruned summary.
Changes:
- Update
WQSummary::SetPruneto cache the active source window (left,right, andtail) before writing intodata_, preventing read-after-write corruption in the in-place compaction loop. - Add a test-only out-of-place reference prune implementation and multiple regression/property-style tests to validate the in-place prune behavior across a variety of geometries.
- Add a deterministic regression test targeting the reported aliasing geometry that previously could violate the “strictly increasing values” invariant.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
src/common/quantile.h |
Fixes WQSummary::SetPrune in-place compaction by caching source entries used by later iterations. |
tests/cpp/common/test_quantile.cc |
Adds focused SetPrune regression and reference-comparison tests, including enumerated, randomized, and boundary geometries. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
RAMitchell
marked this pull request as ready for review
May 11, 2026 09:50
trivialfis
approved these changes
May 11, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fix
WQSummary::SetPruneso its in-place compaction does not corrupt source entries that are still needed by later loop iterations.The previous implementation read and wrote through the same
data_buffer. For some rank geometries, an earlier output write could overwritesrc_data[i], and a later decision would then read the corrupted slot. This could produce duplicate value entries in the pruned summary, violating the strictly increasing value invariant.This keeps
SetPruneallocation-free by caching the active source window (left,right, andtail) before writes can clobber those entries.Tests
Added focused coverage for
SetPrune:Validated locally:
cmake --build build-cpu --target testxgboost -j35./build-cpu/testxgboost --gtest_filter='Quantile.SetPrune*'./build-cpu/testxgboost --gtest_filter=Quantile.*./build-cpu/testxgboost --gtest_filter='*QuantileSummaryTest*:*QuantileContainerTest*'Performance
Compared against
upstream/masterwith a standalone microbenchmark that repeatedly restores pre-generated summaries and callsSetPrune.The in-place cached-entry fix is allocation-free but slightly slower for larger summaries. Pinned-core net prune timing was approximately:
32 -> 16128 -> 32512 -> 642048 -> 2568192 -> 512I also tried a lazy-cache variant that only cached entries when a write would overwrite the active read window. It passed the same tests but benchmarked worse due to extra branch/state overhead, so this PR keeps the simpler cached-window implementation.