myl7/fss 2.0.0
Function secret sharing (FSS) primitives including distributed point/comparison function (DPF/DCF)
Loading...
Searching...
No Matches
cuckoo_hash.cuh
Go to the documentation of this file.
1// SPDX-License-Identifier: Apache-2.0
19
20#pragma once
21#include <cuda_runtime.h>
22#include <cmath>
23#include <limits>
24#include <span>
25#include <utility>
26#include <random>
27#include <fss/prp.cuh>
28#include <stdexcept>
29
30namespace fss::cuckoo_hash {
31
46constexpr int ChBucket(int t, int lambda);
47
48namespace detail {
49
50// Candidate count that callers without access to the schemes' private constant
51// use. The three-choice construction fixes the count; the schemes pass theirs.
52inline constexpr int kDefaultCuckooChoices = 3;
53
62constexpr int kRejectedBucket = std::numeric_limits<int>::max();
63
65struct LocateResult {
66 int bucket;
67 __uint128_t index;
68};
69
76template <typename Prp, typename In>
77 requires Permutable<Prp>
78struct PrpHash {
79 Prp prp;
80
103 __host__ __device__ LocateResult Locate(int4 sigma, In x, int k, __uint128_t n,
104 __uint128_t b_size, int choices = kDefaultCuckooChoices);
105};
106
113template <typename Prp, typename In>
114 requires Permutable<Prp>
115struct Compact {
116 Prp prp;
117
133 int Run(std::span<const In> as, int m, int4 sigma, __uint128_t n, __uint128_t b_size, int max_evictions,
134 std::span<std::pair<int, int>> table);
135};
136
137} // namespace detail
138
139// -----------------------------------------------------------------------------
140
141namespace detail {
142
143constexpr double Log2(double x) {
144 if (x <= 0) return -1e308;
145 int e = 0;
146 double m = x;
147 while (m >= 2.0) {
148 m /= 2.0;
149 ++e;
150 }
151 while (m < 1.0) {
152 m *= 2.0;
153 --e;
154 }
155 double y = (m - 1.0) / (m + 1.0);
156 double y2 = y * y, sum = 0, term = y;
157 for (int k = 0; k < 40; ++k) {
158 sum += term / (2 * k + 1);
159 term *= y2;
160 }
161 return e + 2.0 * sum / 0.6931471805599453;
162}
163
164constexpr double Ceil(double x) {
165 auto i = static_cast<long long>(x);
166 return (x > static_cast<double>(i)) ? static_cast<double>(i + 1) : static_cast<double>(i);
167}
168
181__host__ __device__ constexpr __uint128_t CeilDiv(__uint128_t num, __uint128_t den) {
182 return (num - 1) / den + 1;
183}
184
199__host__ __device__ constexpr __uint128_t BucketIndexBound(__uint128_t n, int choices, __uint128_t bucket_size) {
200 return CeilDiv(n * choices, bucket_size);
201}
202
217__host__ __device__ constexpr __uint128_t MinBucketSize(__uint128_t n, int choices, __uint128_t bucket_count) {
218 return CeilDiv(n * choices, bucket_count);
219}
220
238__host__ __device__ constexpr bool ValidBucketSize(__uint128_t b_size_rt,
239 __uint128_t bucket_size_min, int bucket_bits) {
240 return b_size_rt >= bucket_size_min && b_size_rt <= (__uint128_t{1} << bucket_bits);
241}
242
243} // namespace detail
244
245constexpr int ChBucket(int t, int lambda) {
246 // Remark 1 applies for t >= 30.
247 // For smaller t, the full Lemma 5 formula with Normal CDF factors is needed,
248 // but the paper considers t >= 30 to capture nearly all practical use cases.
249 if (t < 30)
250 throw std::invalid_argument("cuckoo_hash::ChBucket: t must be at least 30");
251 double td = static_cast<double>(t);
252 double e = (static_cast<double>(lambda) + 130.0 + detail::Log2(td)) / 123.5;
253 return static_cast<int>(detail::Ceil(e * td));
254}
255
256namespace detail {
257
258template <typename Prp, typename In>
259 requires Permutable<Prp>
260inline __host__ __device__ LocateResult PrpHash<Prp, In>::Locate(
261 int4 sigma, In x, int k, __uint128_t n, __uint128_t b_size, int choices) {
262 __uint128_t val = static_cast<__uint128_t>(x) + n * k;
263 __uint128_t domain_size = n * static_cast<__uint128_t>(choices);
264 __uint128_t y = prp.Permu(sigma, val, domain_size);
265
266 // Compare the quotient in __uint128_t: a zero b_size has no bucket at all,
267 // and an over-int quotient must saturate rather than wrap negative through
268 // the cast, because the callers filter buckets with `bucket >= bound` and
269 // would accept a negative value before indexing the per-bucket key arrays.
270 if (b_size == 0) {
271 return {kRejectedBucket, 0};
272 }
273 __uint128_t bucket = y / b_size;
274 if (bucket > static_cast<__uint128_t>(kRejectedBucket)) {
275 return {kRejectedBucket, 0};
276 }
277 return {static_cast<int>(bucket), y % b_size};
278}
279
280template <typename Prp, typename In>
281 requires Permutable<Prp>
282inline int Compact<Prp, In>::Run(std::span<const In> as, int m, int4 sigma, __uint128_t n,
283 __uint128_t b_size, int max_evictions, std::span<std::pair<int, int>> table) {
284 PrpHash<Prp, In> hasher{prp};
285 int t = static_cast<int>(as.size());
286
287 // Initialize table to empty.
288 for (int i = 0; i < m; ++i) {
289 table[i] = {-1, -1};
290 }
291
292 std::mt19937 rng(42);
293
294 for (int omega = 0; omega < t; ++omega) {
295 int cur_idx = omega;
296 int cur_k = static_cast<int>(rng() % 3);
297 int evictions = 0;
298
299 for (;;) {
300 auto [bucket, _index] = hasher.Locate(sigma, as[cur_idx], cur_k, n, b_size);
301 // Clamp bucket to [0, m).
302 bucket = bucket % m;
303
304 if (table[bucket].first == -1) {
305 // Empty slot found.
306 table[bucket] = {cur_idx, cur_k};
307 break;
308 }
309
310 // Evict the existing entry and take its place.
311 int evicted_idx = table[bucket].first;
312 table[bucket] = {cur_idx, cur_k};
313
314 // The evicted element becomes the current element with a new random hash.
315 cur_idx = evicted_idx;
316 cur_k = static_cast<int>(rng() % 3);
317
318 ++evictions;
319 if (evictions > max_evictions) {
320 return 1;
321 }
322 }
323 }
324
325 return 0;
326}
327
328} // namespace detail
329
330} // namespace fss::cuckoo_hash
Small-domain pseudorandom permutation (PRP) interface.
Definition prp.cuh:24
constexpr int kRejectedBucket
Bucket identifier of a rejected candidate.
Definition cuckoo_hash.cuh:62
constexpr __uint128_t MinBucketSize(__uint128_t n, int choices, __uint128_t bucket_count)
Smallest bucket domain size that keeps every located bucket in range.
Definition cuckoo_hash.cuh:217
constexpr bool ValidBucketSize(__uint128_t b_size_rt, __uint128_t bucket_size_min, int bucket_bits)
Test a key's runtime bucket domain size against a configuration.
Definition cuckoo_hash.cuh:238
constexpr __uint128_t CeilDiv(__uint128_t num, __uint128_t den)
Ceiling division of two positive integers, in the wrap-free form.
Definition cuckoo_hash.cuh:181
constexpr __uint128_t BucketIndexBound(__uint128_t n, int choices, __uint128_t bucket_size)
Exclusive upper bound on the bucket index of a located candidate.
Definition cuckoo_hash.cuh:199
constexpr int ChBucket(int t, int lambda)
Estimate the number of buckets for three-choice Cuckoo hashing.
Definition cuckoo_hash.cuh:245
Pseudorandom permutation (PRP) interface.