Skip to content

Fix in-place SetPrune aliasing#12202

Merged
RAMitchell merged 3 commits into
dmlc:masterfrom
RAMitchell:codex/quantile-setprune-aliasing
May 11, 2026
Merged

Fix in-place SetPrune aliasing#12202
RAMitchell merged 3 commits into
dmlc:masterfrom
RAMitchell:codex/quantile-setprune-aliasing

Conversation

@RAMitchell

Copy link
Copy Markdown
Member

Summary

Fix WQSummary::SetPrune so 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 overwrite src_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 SetPrune allocation-free by caching the active source window (left, right, and tail) before writes can clobber those entries.

Tests

Added focused coverage for SetPrune:

  • deterministic regression for the reported aliasing geometry
  • out-of-place reference implementation used only in tests
  • exhaustive small integer geometries
  • randomized larger geometries
  • targeted boundary-shaped geometries around zero/nonzero weights

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/master with a standalone microbenchmark that repeatedly restores pre-generated summaries and calls SetPrune.

The in-place cached-entry fix is allocation-free but slightly slower for larger summaries. Pinned-core net prune timing was approximately:

size -> maxsize upstream fixed delta
32 -> 16 237.8 ns 228.3 ns 4.0% faster
128 -> 32 649.5 ns 651.5 ns 0.3% slower
512 -> 64 1562.1 ns 1627.4 ns 4.2% slower
2048 -> 256 6295.7 ns 6613.7 ns 5.1% slower
8192 -> 512 14446.4 ns 16952.9 ns 17.4% slower

I 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.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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::SetPrune to cache the active source window (left, right, and tail) before writing into data_, 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.

Comment thread tests/cpp/common/test_quantile.cc
@RAMitchell
RAMitchell marked this pull request as ready for review May 11, 2026 09:50
@RAMitchell
RAMitchell requested a review from trivialfis May 11, 2026 09:50
@RAMitchell
RAMitchell merged commit f330d09 into dmlc:master May 11, 2026
80 checks passed
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.

3 participants