Repository navigation
Use multiply-high range reduction for probing - #869
Conversation
| if constexpr (cuda::std::is_same_v<HashType, cuda::std::array<std::uint64_t, 2>>) { | ||
| #if !defined(CUCO_HAS_INT128) | ||
| static_assert(false, | ||
| "CUCO_HAS_INT128 undefined. Need unsigned __int128 type when sanitizing " | ||
| "cuda::std::array<std::uint64_t, 2>"); | ||
| #endif | ||
| unsigned __int128 ret{}; | ||
| memcpy(&ret, &hash, sizeof(unsigned __int128)); | ||
| magnitude = to_positive<SizeType>(ret); | ||
| return hash_to_index<SizeType>(static_cast<unsigned_size_type>(hash[0]), upper_bound); | ||
| } else if constexpr (hash_bytes > 8) { | ||
| static_assert(cuda::std::is_integral_v<HashType> and hash_bytes == 16); | ||
| return hash_to_index<SizeType>(static_cast<unsigned_size_type>(hash), upper_bound); |
There was a problem hiding this comment.
nit: cudax (NVIDIA/cccl#12360) uses the high word of a 128-bit hash, while this uses the low word. Either works, but let's make them match.
|
The +15.81% slowdown for LP find hits at 800K/I32/90% is surprising, since modulo and multiply-high spread a good hash equally well. Does it show up with a different key seed? |
The slowdown is an artifact. I ran a number of different seeds and none of them showed this level of slowdown. Note that this particular configuration (very high load factor with linear probing) is prone to having a handful of probe chains that are exceptionally long and hold the kernel hostage until they finish. No one should be using LP at >70% LF anyways. |
Match cudax::cuco (NVIDIA/cccl#12360): a 128-bit hash contributes its high 64 bits, which are then reduced like a 64-bit hash.
lol, that's fair. |
Modulo by a runtime bucket count is expensive, and probing does not need the exact remainder: it only needs a well-distributed index in
[0, n). This uses multiply-high instead of modulo for initial bucket selection and double-hashing steps.For a uniform
w-bit hash, bothh % nandfloor(h * n / 2^w)assign eitherfloor(2^w / n)orceil(2^w / n)hash values to each bucket. They provide the same range and bucket balance, but assign individual keys to different buckets.Codegen
Whole-kernel static SASS instruction counts on CUDA 13.4,
sm_120:Counts include setup and cold paths, excluding NOP padding; they are not instructions executed per lookup. The index-mapping test also drops from 18 to 12 registers.
Performance
RTX PRO 6000 Blackwell Max-Q, CUDA 13.4, GCC 14, Release,
sm_120. Default XXH32, 32/64-bit keys, 800K/8M inputs. Three paired runs against27302294; negative is faster. Summary values are geometric-mean GPU time changes.Each delta compares multiply-high against modulo with the same probing scheme, not linear probing against double hashing.
Maps use their default CG=4 and bucket size 1 for both probing schemes. Multimaps use their defaults: double hashing, CG=8, bucket size 2. All extents are 64-bit.
Map: 800K keys
LP = linear probing, DH = double hashing. Per-configuration GPU time changes use the median of three runs. Find columns use either all hits or all misses.
Map: 8M keys
The 800K/I32/90% linear-probing lookup regression comes primarily from longer probe tails in the changed table layout, as confirmed by profiling and identical-code replay. It does not appear in this double-hashing sweep. Differences around 1% should be treated cautiously.