sleeepyjack opened a new pull request, #15:
URL: https://github.com/apache/datasketches-cuda/pull/15
## Summary
Draft alternative to #12 and #14.
This replaces the Theta screen/chunk/sort update path with a persistent grid
that
keeps an exact open-addressing set in shared memory. Each resident block
owns one
input partition, while its 32 warps process independent subranges. Accepted
hashes
remain local to the SM until the table reaches its configured load limit.
When a table needs reduction, the block finds the exact rank-k boundary with
eight
8-bit histogram passes, compacts the hashes below that boundary, clears the
table,
and rebuilds it with the retained bottom-k. The new theta is published with a
device-scoped atomic min so other blocks can reject more input without a
global
rendezvous.
After all blocks finish, each emits at most `k + 1` values. A single
device-wide
CUB radix sort and unique pass combines those candidates with the sketch's
previous
retained hashes.
This implementation does not directly depend on cuCollections. HLL still
uses the
cuco components shipped through CCCL.
## Block-local algorithm
- 1024 threads per block, one target block per SM.
- Shared-memory capacity is derived from
`cudaDevAttrMaxSharedMemoryPerBlockOptin` and
`cudaDevAttrMaxSharedMemoryPerMultiprocessor`; it is not hardcoded.
- 32 independent warp workers use `elect.sync` for leader work and warp
ballots to
aggregate successful insertions.
- The set stores already-hashed 64-bit values with linear probing and exact
duplicate detection.
- The empty sentinel, zero hash, and values outside theta are rejected before
insertion.
- The occupancy limit is 80%. Capacity reserves enough space for one
worst-case
in-flight round from all 1024 threads.
- Workers periodically read the device-wide theta, but otherwise communicate
only
through block-scoped counters.
- CTA phase decisions use `__syncthreads_or`, avoiding a race where one warp
could
enter rebuild while another was still reading the previous decision.
- Scoped compiler atomics are used for set loads/CAS and device theta
publication;
CUDA-C block atomics are retained for shared counters because they lower
directly
to shared-memory `ATOMS` instructions.
## Comparison with the existing PRs
### #12: fused screen plus best-effort duplicate cache
#12 hashes and screens into an input-sized global output buffer. It uses
`__match_any_sync` and a small direct-mapped shared cache to remove
inexpensive
duplicates before sorting. Correctness does not depend on the cache because
remaining duplicates are removed globally.
This PR instead keeps an exact set and performs repeated rank-k reduction
entirely
inside each resident block. Global memory receives only `O(blocks * k)`
candidates.
### #14: cuco exact set inside the screen kernel
#14 replaces #12's direct-mapped cache with a 2048-slot exact cuco set and
bulk
retrieval, but retains the same input-sized screen output and global sort
pipeline.
It also depends on private cuco open-addressing internals to obtain an 8-byte
key-only slot.
This PR uses a small purpose-built set with no direct cuco dependency and
uses the
set as the block's persistent retained state, rather than only as a duplicate
filter for an `O(N)` output.
## 16 GiB comparison
RTX PRO 6000 Blackwell Max-Q, CUDA 13.3, SM120, U64 unique input,
`2,147,483,648` keys, `lg_k=12`, 50 isolated samples:
| Approach | Cold update | Warm update |
|---|---:|---:|
| #12 direct-mapped screen | 12.790 ms | **11.762 ms** |
| #14 cuco exact-set screen | 13.372 ms | 12.163 ms |
| This PR, persistent exact set | **12.685 ms** | 12.049 ms |
The differences around one to four percent have similar run-to-run noise. The
persistent path is approximately 0.8% faster cold and 2.4% slower warm than
#12,
and 5.1% faster cold and 0.9% faster warm than #14.
Nsight Compute speed-of-light measurements for the primary scanning kernel:
| Approach | Cold DRAM | Cold kernel | Warm DRAM | Warm kernel |
|---|---:|---:|---:|---:|
| #12 screen kernel | **90.58%** | **11.14 ms** | **91.54%** | **10.98 ms** |
| This PR persistent kernel | 85.53% | 11.80 ms | 90.63% | 11.16 ms |
The persistent design is near the same steady-state bandwidth limit, while
its
cold path spends more time performing local exact reductions.
## Memory footprint
The input allocation itself is excluded below.
| Approach | Additional global memory for a 16 GiB update | Shared memory |
|---|---:|---:|
| #12 | At least one input-sized screen buffer: about 16 GiB, plus survivor
sort/merge temporaries | 8 KiB direct-mapped cache per block |
| #14 | At least one input-sized screen buffer: about 16 GiB, plus survivor
sort/merge temporaries | 16 KiB cuco set per block |
| This PR | About 17.8 MiB of persistent candidate/alternate/unique storage
on this 188-SM GPU, plus small CUB temporary storage | 101.4 KiB per resident
block |
This removes the `O(N)` DRAM scratch allocation, which is the primary
motivation
for the design. The current fixed scratch allocation is still a known
problem for
the future many-sketch use case: every sketch reserves capacity for a full
188-block grid even when an update would use one block. A follow-up should
right-size or pool this scratch before thousands of sketches are expected to
coexist.
## Known draft limitations
- Only `lg_k=12` is currently supported. The existing benchmark definitions
still
contain larger `LgK` values and must be overridden to 12.
- The kernel requires SM90+ and the current CUDA compiler atomic builtins.
This is
not yet enforced by CMake, and the repository-level CUDA 12.0 compatibility
statement has not been revised.
- The remaining performance gap to #12's standalone screen kernel is
primarily in
the cold local-reduction path.
## Validation
- Fresh CUDA 13.3 / SM120 build after merging current `upstream/main`
- SM90 compile succeeds
- All 16 test executables pass
- Persistent-update boundary tests cover empty input, 1, 31/32/33,
1023/1024/1025, `k-1/k/k+1`, load-limit boundaries, shuffled odd-sized
input,
duplicates, zero, theta boundary, and the empty sentinel
- Compute Sanitizer memcheck: zero errors
- Compute Sanitizer racecheck: zero hazards
- Compute Sanitizer synccheck: zero errors
- clang-format hook passes
Related: #12, #14
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]