cuDDL
Loading...
Searching...
No Matches
hash.cuh
Go to the documentation of this file.
1#pragma once
2
3#include <cuda/std/bit>
4#include <cuda/std/cstdint>
5
6namespace cuddl::detail {
7
9__host__ __device__ constexpr uint64_t splitmix64(uint64_t value) noexcept {
10 value ^= value >> 30;
11 value *= 0xbf58476d1ce4e5b9ULL;
12 value ^= value >> 27;
13 value *= 0x94d049bb133111ebULL;
14 return value ^ (value >> 31);
15}
16
18constexpr uint64_t seed = 42;
19
21__host__ __device__ constexpr uint64_t hash_kmer(uint64_t packed_kmer) noexcept {
22 return splitmix64(packed_kmer ^ seed);
23}
24
26template <size_t BucketCount>
27__host__ __device__ constexpr size_t bucket_of(uint64_t hash) noexcept {
28 static_assert(BucketCount != 0 && (BucketCount & (BucketCount - 1)) == 0);
29 return hash & (BucketCount - 1);
30}
31
33constexpr uint64_t PRIME64_1 = 11400714785074694791ULL;
34constexpr uint64_t PRIME64_2 = 14029467366897019727ULL;
35constexpr uint64_t PRIME64_3 = 1609587929392839161ULL;
36constexpr uint64_t PRIME64_4 = 9650029242287828579ULL;
37constexpr uint64_t PRIME64_5 = 2870177450012600261ULL;
38
40constexpr __host__ __device__ __forceinline__ uint64_t rotl64(uint64_t x, int8_t r) {
41 return cuda::std::rotl(x, static_cast<int>(r));
42}
43
45template <typename T>
46__host__ __device__ __forceinline__ T load_chunk(const uint8_t* data, uint64_t index) {
47 T chunk;
48 memcpy(&chunk, data + index * sizeof(T), sizeof(T));
49 return chunk;
50}
51
53constexpr __host__ __device__ __forceinline__ uint64_t finalize(uint64_t h) {
54 h ^= h >> 33;
55 h *= PRIME64_2;
56 h ^= h >> 29;
57 h *= PRIME64_3;
58 h ^= h >> 32;
59 return h;
60}
61
63__host__ __device__ inline uint64_t xxhash64(const uint8_t* bytes, uint64_t size, uint64_t seed) {
64 uint64_t offset = 0;
65 uint64_t h64;
66
67 // Process 32-byte chunks
68 if (size >= 32) {
69 uint64_t limit = size - 32;
70 uint64_t v1 = seed + PRIME64_1 + PRIME64_2;
71 uint64_t v2 = seed + PRIME64_2;
72 uint64_t v3 = seed;
73 uint64_t v4 = seed - PRIME64_1;
74
75 do {
76 const uint64_t pipeline_offset = offset / 8;
77 v1 += load_chunk<uint64_t>(bytes, pipeline_offset + 0) * PRIME64_2;
78 v1 = rotl64(v1, 31);
79 v1 *= PRIME64_1;
80 v2 += load_chunk<uint64_t>(bytes, pipeline_offset + 1) * PRIME64_2;
81 v2 = rotl64(v2, 31);
82 v2 *= PRIME64_1;
83 v3 += load_chunk<uint64_t>(bytes, pipeline_offset + 2) * PRIME64_2;
84 v3 = rotl64(v3, 31);
85 v3 *= PRIME64_1;
86 v4 += load_chunk<uint64_t>(bytes, pipeline_offset + 3) * PRIME64_2;
87 v4 = rotl64(v4, 31);
88 v4 *= PRIME64_1;
89 offset += 32;
90 } while (offset <= limit);
91
92 h64 = rotl64(v1, 1) + rotl64(v2, 7) + rotl64(v3, 12) + rotl64(v4, 18);
93
94 v1 *= PRIME64_2;
95 v1 = rotl64(v1, 31);
96 v1 *= PRIME64_1;
97 h64 ^= v1;
98 h64 = h64 * PRIME64_1 + PRIME64_4;
99
100 v2 *= PRIME64_2;
101 v2 = rotl64(v2, 31);
102 v2 *= PRIME64_1;
103 h64 ^= v2;
104 h64 = h64 * PRIME64_1 + PRIME64_4;
105
106 v3 *= PRIME64_2;
107 v3 = rotl64(v3, 31);
108 v3 *= PRIME64_1;
109 h64 ^= v3;
110 h64 = h64 * PRIME64_1 + PRIME64_4;
111
112 v4 *= PRIME64_2;
113 v4 = rotl64(v4, 31);
114 v4 *= PRIME64_1;
115 h64 ^= v4;
116 h64 = h64 * PRIME64_1 + PRIME64_4;
117 } else {
118 h64 = seed + PRIME64_5;
119 }
120
121 h64 += size;
122
123 // Process remaining 8-byte chunks
124 if ((size % 32) >= 8) {
125 for (; offset <= size - 8; offset += 8) {
126 uint64_t k1 = load_chunk<uint64_t>(bytes, offset / 8) * PRIME64_2;
127 k1 = rotl64(k1, 31) * PRIME64_1;
128 h64 ^= k1;
129 h64 = rotl64(h64, 27) * PRIME64_1 + PRIME64_4;
130 }
131 }
132
133 // Process remaining 4-byte chunks
134 if ((size % 8) >= 4) {
135 for (; offset <= size - 4; offset += 4) {
136 h64 ^= (load_chunk<uint32_t>(bytes, offset / 4) & 0xffffffffULL) * PRIME64_1;
137 h64 = rotl64(h64, 23) * PRIME64_2 + PRIME64_3;
138 }
139 }
140
141 // Process remaining bytes
142 if (size % 4) {
143 while (offset < size) {
144 h64 ^= (bytes[offset] & 0xff) * PRIME64_5;
145 h64 = rotl64(h64, 11) * PRIME64_1;
146 ++offset;
147 }
148 }
149
150 return finalize(h64);
151}
152
154template <typename T>
155__host__ __device__ inline uint64_t xxhash64(const T& key, uint64_t seed = 0) {
156 return xxhash64(reinterpret_cast<const uint8_t*>(&key), sizeof(T), seed);
157}
158
159} // namespace cuddl::detail
constexpr uint64_t PRIME64_3
Definition hash.cuh:35
constexpr uint64_t PRIME64_1
xxHash-64 round primes.
Definition hash.cuh:33
__host__ __device__ __forceinline__ T load_chunk(const uint8_t *data, uint64_t index)
Loads a chunk of type T from data at byte offset index*sizeof(T).
Definition hash.cuh:46
__host__ __device__ constexpr uint64_t splitmix64(uint64_t value) noexcept
SplitMix64 finalizer, applied to a packed k-mer XOR the domain seed.
Definition hash.cuh:9
constexpr uint64_t seed
Domain separation constant folded into every packed k-mer before hashing.
Definition hash.cuh:18
__host__ __device__ uint64_t xxhash64(const uint8_t *bytes, uint64_t size, uint64_t seed)
Computes xxHash-64 over a byte range without allocating or copying it.
Definition hash.cuh:63
constexpr __host__ __device__ __forceinline__ uint64_t finalize(uint64_t h)
Applies the xxHash-64 final mixing (avalanche) step.
Definition hash.cuh:53
__host__ __device__ constexpr uint64_t hash_kmer(uint64_t packed_kmer) noexcept
Hashes a canonical packed k-mer into the value space used by DDL registers.
Definition hash.cuh:21
constexpr uint64_t PRIME64_2
Definition hash.cuh:34
constexpr __host__ __device__ __forceinline__ uint64_t rotl64(uint64_t x, int8_t r)
Rotates x left by r bits.
Definition hash.cuh:40
constexpr uint64_t PRIME64_5
Definition hash.cuh:37
constexpr uint64_t PRIME64_4
Definition hash.cuh:36
__host__ __device__ constexpr size_t bucket_of(uint64_t hash) noexcept
Selects the register bucket for hash given a power-of-two BucketCount.
Definition hash.cuh:27