Skip to content

feat(cudf): Restore Spark xxhash64 and runtime Bloom filters - #102

Open
sperlingxx wants to merge 2 commits into
devfrom
staging-cudf-xxhash64-bloom-filter-fused
Open

feat(cudf): Restore Spark xxhash64 and runtime Bloom filters#102
sperlingxx wants to merge 2 commits into
devfrom
staging-cudf-xxhash64-bloom-filter-fused

Conversation

@sperlingxx

@sperlingxx sperlingxx commented Aug 25, 2026

Copy link
Copy Markdown
Collaborator

Summary

Restore the Spark cuDF expression and aggregation paths required by runtime Bloom filters:

  • register Spark-compatible single-column xxhash64_with_seed;
  • implement might_contain for serialized Spark/Velox Bloom filters;
  • support reduce-only bloom_filter_agg for raw and intermediate/final aggregation;
  • retain focused expression, aggregation, and selection coverage; and
  • fail closed for multi-column xxhash64, whose cuDF combination semantics differ from Spark's iterative seed chaining.

The Bloom-filter probe and aggregation implementations deliberately use host copies and synchronization for compatibility. This PR does not claim a GPU-native performance implementation.

Why

Current dev fails TPC-H Q20 before execution when CPU fallback is disabled because xxhash64_with_seed cannot be replaced. Ferdinand's July runtime already contained this support; it was lost from the current development line during a later upstream merge.

Validation

  • git diff --check passes at 668f0bc6ca37d1e8456c8389b347735040a1fb37.
  • The fused feature head 48615d8326b7077586137374c7e56d3611ce40d3 compiled in NVL72 Slurm/Pyxis build job 11596, and the bundle passed check-deploy qualification.
  • Four-peer job 11606 removed the prior hard replacement failure; Q20 completed all three attempts in 4.745 / 4.717 / 4.702 seconds.
  • The follow-up commit adds a focused selector test proving one data column remains supported and multiple data columns are rejected.

The runtime artifact predates the final fail-closed selector commit, but Q20 uses the unchanged single-column path. Result comparison was disabled in job 11606, so a CPU-oracle correctness comparison and performance qualification remain separate gates.

AI disclosure

Generated-by: Codex (GPT-5)

Fixes #101

- Register xxhash64_with_seed and might_contain in the Spark cuDF expression adapter.
- Add reduce-only bloom_filter_agg support for raw and intermediate/final aggregation.
- Cover expression selection, xxhash64 parity, might_contain, and Bloom-filter aggregation.
- Include the CudfReduce parenthesis correction required for GCC compilation.
- Fuse 34d5c22 and 2842252 on dev 28798f6.
- Reject multi-column xxhash64_with_seed regardless of CPU fallback because cuDF does not implement Spark iterative seed chaining.

- Preserve the single-column GPU path required by the Q20 runtime Bloom-filter workflow.

- Cover supported single-column and rejected multi-column expression selection.

Fixes #101
@github-actions github-actions Bot added the cudf label Aug 25, 2026
@sperlingxx sperlingxx changed the title [cuDF] Restore Spark xxhash64 and runtime Bloom filters feat(cudf): Restore Spark xxhash64 and runtime Bloom filters Aug 25, 2026

std::vector<int64_t> hostKeys(static_cast<size_t>(numRows));
if (numRows > 0) {
copyKeysToHost(inputView, hostKeys, stream);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Plz add some comments why we need to copy keys to host.

@winningsix

Copy link
Copy Markdown
Collaborator

Do we have some perf number before vs. after? B/c previously we skipped bloom filter and use some plan optimizer avoiding bloom filter.

// seed-chaining (rapidsai/cudf#21720). Always reject this shape so callers
// either use CPU fallback or fail closed instead of returning wrong hashes.
const bool hasMultipleDataColumns = expr->inputs().size() > 2;
return !hasMultipleDataColumns;

@thirtiseven thirtiseven Aug 25, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Blocking] Please gate the single-column path by input type (or normalize the input before hashing). The registered signature accepts any, but libcudf hashes the physical storage width while Spark widens TINYINT, SMALLINT, and BOOLEAN to 32 bits; libcudf also preserves the sign bit of -0.0, whereas Spark normalizes it to +0.0. For example, with seed 42, hashing TINYINT(1) as one byte yields 6668291691252061002, while Spark's 32-bit semantics yield -6698625589789238999. These shapes currently pass canEvaluate() and silently return incorrect hashes. Please restrict selection to parity-proven types or implement Spark-compatible widening/normalization, with coverage for every advertised type.

bloomIsNull_ = true;
return;
}
auto serialized = bloomValue->as<SimpleVector<StringView>>()->valueAt(0);

@thirtiseven thirtiseven Aug 25, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Blocking] Please validate the serialized value before passing it to BloomFilterView/mayContain. The CPU implementation checks serialized->size() >= BloomFilterView::kSerializedHeaderSize; this path stores any non-null literal without that check. A short value beginning with the valid version byte (for example, a one-byte 0x01) makes BloomFilterView read the four-byte size past the buffer, which is undefined behavior instead of the CPU path's user error. Please add the same minimum-header check here and a malformed-literal test.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[cuDF] Restore Spark xxhash64 and runtime Bloom-filter execution

3 participants