myl7/fss 2.0.0
Function secret sharing (FSS) primitives including distributed point/comparison function (DPF/DCF)
Loading...
Searching...
No Matches
grotto_dcf.cuh
Go to the documentation of this file.
1// SPDX-License-Identifier: Apache-2.0
34
35#pragma once
36#include <cuda_runtime.h>
37#include <type_traits>
38#include <cstddef>
39#include <cassert>
40#ifdef _OPENMP
41#include <omp.h>
42#endif
43#include <fss/dpf.cuh>
44#include <fss/group/bytes.cuh>
45#include <fss/prg.cuh>
46#include <fss/util.cuh>
47#include <fss/constraints.cuh>
48#include <fss/compat.cuh>
49#include <cuda/std/span>
50#include <memory>
51#include <utility>
52
53#include <cstdint>
54#include <limits>
55#if defined(__CUDACC__)
56#include <cub/device/device_scan.cuh>
57#include <stdexcept>
58#endif
59
60namespace fss {
61
62template <typename Scheme>
63class OwnedParityTree;
64
65namespace detail {
66class SchemeAccess;
67
68// Split correction-word view shared by the host and device split paths. The key is an
69// underlying DPF key with beta equal to zero, so layer l is read from
70// cws[l*stride] with the right-control correction in bit l of packed_tr. Bits at
71// or above in_bits are ignored because no control correction is read from the
72// terminal entry. operator[] recovers the Cw the shared algorithms consume, so
73// their bodies stay identical on the Cw and split paths.
74template <typename Cw, typename In>
75struct GrottoDcfCwsView {
76 using Control = In;
77
78 const int4 *cws;
79 In packed_tr;
80 size_t stride;
81
82 __host__ __device__ inline Cw operator[](int l) const {
83 return Cw{cws[static_cast<size_t>(l) * stride], ((packed_tr >> l) & 1) != 0};
84 }
85};
86
87} // namespace detail
88
101template <int in_bits, typename Prg, typename In = uint>
102 requires(ValidForInputBits<In, in_bits> && Prgable<Prg, 2>)
105
106public:
107 using Cw = typename DpfType::Cw;
108
110 static constexpr int kCwCount = in_bits + 1;
111
112 using Input = In;
113
119 __host__ __device__ explicit GrottoDcf(Prg prg);
120
144 __host__ __device__ void Gen(cuda::std::span<int4> cws, In &tr,
145 cuda::std::span<const int4, 2> s0s, In a, size_t stride = 1);
146
172 __host__ __device__ bool Eval(bool b, int4 s0, cuda::std::span<const int4> cws,
173 In tr, In x, size_t stride = 1);
174
190 const bool *p;
191 };
192
217 void Preprocess(bool b, int4 s0, cuda::std::span<const int4> cws, In tr,
218 bool p[], int par_depth = 0, size_t stride = 1);
219
242 cuda::std::span<const int4> cws, In tr, int par_depth = 0, size_t stride = 1);
243
253 __host__ __device__ static bool Eval(ParityTreeView pt, In x);
254
264 static void EvalAll(ParityTreeView pt, bool ys[]);
265
290 void EvalAll(bool b, int4 s0, cuda::std::span<const int4> cws, In tr,
291 bool ys[], int par_depth = 0, size_t stride = 1);
292
315 template <int frontier_depth = 16, int threads_per_block = 256>
316 __host__ cudaError_t PreprocessGpu(bool b, int4 s0, cuda::std::span<const int4> cws,
317 In tr, cuda::std::span<bool> tree, cudaStream_t stream = 0, size_t stride = 1) const
318 requires(in_bits < sizeof(size_t) * 8);
319
344 template <int frontier_depth = 16, int threads_per_block = 256>
345 __host__ cudaError_t EvalAllGpu(bool b, int4 s0, cuda::std::span<const int4> cws,
346 In tr, cuda::std::span<bool> ys, cudaStream_t stream = 0, size_t stride = 1) const
347 requires(in_bits < sizeof(size_t) * 8);
348
361 __host__ static cudaError_t EvalAllGpu(ParityTreeView pt,
362 cuda::std::span<bool> ys, cudaStream_t stream = 0)
363 requires(in_bits < sizeof(size_t) * 8);
364
365#if FSS_ENABLE_V1_COMPAT
366 // Deprecated v1.3.0 aggregate-Cw and aggregate-Key compatibility entries; see
367 // fss/compat.cuh. Each converts through a compile-time-sized scratch buffer
368 // and the current split-component method above at stride 1. cws has
369 // in_bits + 1 entries. The Key, ParityTree, and PreprocessedTree aliases
370 // expose compat records under the legacy nested names, while the method
371 // declarations spell the record types so a deprecated name never enters a
372 // member signature. EvalAll() runs sequentially, as the current
373 // split-component default does; the old automatic thread-count split is not
374 // restored.
375
377 [[deprecated("use the split-component Gen(cuda::std::span<int4>, In&, cuda::std::span<const int4, 2>, In, size_t) overload")]]
378 __host__ __device__ void Gen(Cw cws[], const int4 s0s[2], In a);
379
385 using ParityTree [[deprecated("use ParityTreeView, which drops the party tag")]] =
386 compat::detail::ParityTreeRecord<GrottoDcf>;
387
393 using Key [[deprecated("use the split-component Gen/EvalAll/Preprocess with separate cws and tr arrays")]] =
394 compat::detail::CwsKeyRecord<GrottoDcf, Cw, in_bits + 1>;
395
401 using PreprocessedTree [[deprecated("use PreprocessOwned() and OwnedParityTree::View()")]] =
402 compat::detail::PreprocessedTreeRecord<GrottoDcf>;
403
405 [[deprecated("use the split-component Preprocess(bool, int4, cuda::std::span<const int4>, In, bool*, int, size_t) overload")]]
406 void Preprocess(compat::detail::ParityTreeRecord<GrottoDcf> &pt, int4 s0, const Cw cws[]);
407
409 [[deprecated("use the split-component Gen(cuda::std::span<int4>, In&, cuda::std::span<const int4, 2>, In, size_t) overload")]]
410 __host__ __device__ void Gen(compat::detail::CwsKeyRecord<GrottoDcf, Cw, in_bits + 1> &k0,
411 compat::detail::CwsKeyRecord<GrottoDcf, Cw, in_bits + 1> &k1,
412 cuda::std::span<const int4, 2> s0s, In a);
413
415 [[deprecated("use the split-component PreprocessOwned(bool, int4, cuda::std::span<const int4>, In, int, size_t)")]]
416 compat::detail::PreprocessedTreeRecord<GrottoDcf> Preprocess(
417 bool b, const compat::detail::CwsKeyRecord<GrottoDcf, Cw, in_bits + 1> &key);
418
420 [[deprecated("use the ParityTreeView Eval(ParityTreeView, In) overload")]]
421 __host__ __device__ static bool Eval(const compat::detail::ParityTreeRecord<GrottoDcf> &pt, In x);
422
424 [[deprecated("use OwnedParityTree::View() and the ParityTreeView Eval(ParityTreeView, In) overload")]]
425 static bool Eval(const compat::detail::PreprocessedTreeRecord<GrottoDcf> &tree, In x);
426
428 [[deprecated("use the split-component EvalAll(bool, int4, cuda::std::span<const int4>, In, bool[], int, size_t) overload")]]
429 void EvalAll(bool b, int4 s0, const Cw cws[], bool ys[]);
430
432 [[deprecated("use the split-component EvalAll(bool, int4, cuda::std::span<const int4>, In, bool[], int, size_t) overload")]]
433 void EvalAll(bool b, const compat::detail::CwsKeyRecord<GrottoDcf, Cw, in_bits + 1> &key,
434 cuda::std::span<bool> ys);
435#endif // FSS_ENABLE_V1_COMPAT
436
437private:
438 friend class detail::SchemeAccess;
439
440 Prg prg_;
441
442 // Split correction-word view over the caller's array. The host methods and the
443 // split GPU kernels share this one view; see detail::GrottoDcfCwsView.
444 using CwsView = detail::GrottoDcfCwsView<Cw, In>;
445
446 // Shared path evaluation over the Cw array or the split view.
447 template <typename Cws>
448 __host__ __device__ bool EvalCws(bool b, int4 s0, Cws cws, In x);
449
450 // Shared preprocessing over the Cw array or the split view.
451 template <typename Cws>
452 void PreprocessCws(bool b, int4 s0, Cws cws, bool p[], int par_depth);
453
454 // Shared full-domain evaluation over the Cw array or the split view.
455 template <typename Cws>
456 void EvalAllCws(bool b, int4 s0, Cws cws, bool ys[], int par_depth);
457
458 // Shared owner-producing preprocessing over the Cw array or the split view.
459 template <typename Cws>
460 OwnedParityTree<GrottoDcf> PreprocessOwnedCws(
461 bool b, int4 s0, Cws cws, int par_depth);
462
463 // Expand the DPF tree and write leaf control bits.
464 template <typename Cws>
465 void ExpandTree(bool b, int4 s0, Cws cws, bool t[], int par_depth);
466
467 template <typename Cws>
468 void ExpandTreeRec(int4 st, Cws cws, bool t[], size_t l, size_t r, int i, int par_depth_);
469};
470
479template <typename Scheme>
481public:
486 explicit OwnedParityTree(std::unique_ptr<bool[]> p);
487
489 typename Scheme::ParityTreeView View() const &;
490
492 typename Scheme::ParityTreeView View() const && = delete;
493
494private:
495 std::unique_ptr<bool[]> p_;
496};
497
498// -----------------------------------------------------------------------------
499
500namespace gpu::detail {
501
504template <int tree_depth, int frontier_depth, int threads_per_block>
505struct GrottoGpuLaunchShape {
506 static_assert(frontier_depth >= 0, "gpu frontier depth must be nonnegative");
507 static_assert(1 <= threads_per_block && threads_per_block <= 1024 &&
508 (threads_per_block & (threads_per_block - 1)) == 0,
509 "gpu block size must be a power of two in [1,1024]");
510 static constexpr int kFrontierDepth = frontier_depth < tree_depth ? frontier_depth : tree_depth;
511 static_assert(tree_depth - kFrontierDepth <= 8,
512 "gpu frontier leaves more than eight tail levels; specify a larger frontier_depth");
513 static constexpr int kThreadsPerBlockLog2 = [] {
514 int log2 = 0;
515 for (int count = threads_per_block; count > 1; count >>= 1) ++log2;
516 return log2 < kFrontierDepth ? log2 : kFrontierDepth;
517 }();
518 static constexpr int kThreadsPerBlock = 1 << kThreadsPerBlockLog2;
519 static constexpr int kBlockRootDepth = kFrontierDepth - kThreadsPerBlockLog2;
520 static_assert(kBlockRootDepth <= 30, "gpu block index exceeds the signed-int range");
521 static constexpr size_t kBlocks = size_t{1} << (kBlockRootDepth <= 30 ? kBlockRootDepth : 0);
522};
523
524#if defined(__CUDACC__)
525
526struct GrottoGpuBoolXor {
527 __host__ __device__ bool operator()(bool lhs, bool rhs) const { return lhs != rhs; }
528};
529
530static __global__ void GrottoUnpackBitsKernel(const int4 *blocks, bool *leaves, size_t count) {
531 size_t i = static_cast<size_t>(blockIdx.x) * blockDim.x + threadIdx.x;
532 if (i >= count) return;
533 int4 block = blocks[i / 128];
534 unsigned word = (i % 128) / 32;
535 unsigned value = word == 0 ? static_cast<unsigned>(block.x) :
536 word == 1 ? static_cast<unsigned>(block.y) : word == 2 ? static_cast<unsigned>(block.z) : static_cast<unsigned>(block.w);
537 leaves[i] = ((value >> (i % 32)) & 1u) != 0;
538}
539
540inline cudaError_t GrottoUnpackGrid(size_t count, int threads, int &grid) {
541 const size_t blocks = count / threads + (count % threads != 0);
542 if (blocks > static_cast<size_t>(std::numeric_limits<int>::max()))
543 return cudaErrorInvalidConfiguration;
544 grid = static_cast<int>(blocks);
545 return cudaSuccess;
546}
547
548// Split-layout leaf-bits kernel. The correction buffer
549// arrives type-erased and the boundary view reconstructs the per-layer Cw
550// entries, so the traversal stays identical on both paths.
551template <int input_bits, int frontier_depth, int block_root_depth, typename Dpf, typename Prg,
552 typename Cws>
553__device__ void GrottoSplitLeafBitsWork(bool party, int4 s0, const Cws &view, bool *leaves,
554 Prg prg) {
555 constexpr int kTailDepth = input_bits - frontier_depth;
556 constexpr int kBlockSubtreeDepth = frontier_depth - block_root_depth;
557 const int64_t thread_index = static_cast<int64_t>(blockIdx.x) * blockDim.x + threadIdx.x;
558 __shared__ int4 shared_frontier[1 << kBlockSubtreeDepth];
559 if (threadIdx.x == 0) {
560 int4 state = util::SetLsb(s0, party);
561#pragma unroll
562 for (int level = 0; level < block_root_depth; ++level) {
563 bool take_right = (blockIdx.x >> (block_root_depth - 1 - level)) & 1;
564 auto node_prg = prg;
565 const auto cw = view[level];
566 state = fss::detail::SchemeAccess::DpfPathStep<Dpf>(
567 node_prg, state, cw.s, cw.tr, take_right);
568 }
569 shared_frontier[0] = state;
570 }
571 __syncthreads();
572
573#pragma unroll
574 for (int level = 0; level < kBlockSubtreeDepth; ++level) {
575 const bool active = threadIdx.x < (1 << (level + 1));
576 int4 state = {0, 0, 0, 0};
577 if (active) state = shared_frontier[threadIdx.x >> 1];
578 __syncthreads();
579 if (active) {
580 int4 left, right;
581 {
582 auto node_prg = prg;
583 const auto cw = view[block_root_depth + level];
584 fss::detail::SchemeAccess::DpfExpandNode<Dpf>(node_prg, state, cw.s, cw.tr, left, right);
585 }
586 shared_frontier[threadIdx.x] = (threadIdx.x & 1) ? right : left;
587 }
588 __syncthreads();
589 }
590
591 int4 nodes[1 << kTailDepth];
592 nodes[0] = shared_frontier[threadIdx.x];
593 int count = 1;
594#pragma unroll
595 for (int level = frontier_depth; level < input_bits; ++level) {
596#pragma unroll
597 for (int i = count - 1; i >= 0; --i) {
598 int4 left, right;
599 {
600 auto node_prg = prg;
601 const auto cw = view[level];
602 fss::detail::SchemeAccess::DpfExpandNode<Dpf>(node_prg, nodes[i], cw.s, cw.tr, left, right);
603 }
604 nodes[2 * i] = left;
605 nodes[2 * i + 1] = right;
606 }
607 count *= 2;
608 }
609
610 const size_t output_offset = static_cast<size_t>(thread_index) << kTailDepth;
611#pragma unroll
612 for (int i = 0; i < count; ++i) leaves[output_offset + i] = util::GetLsb(nodes[i]);
613}
614template <int input_bits, int frontier_depth, int block_root_depth, typename Dpf, typename Prg,
615 typename Cws>
616__global__ void GrottoSplitLeafBitsKernel(bool party, int4 s0, const void *cws_buf,
617 typename Cws::Control packed_tr, size_t stride, bool *leaves, Prg prg) {
618 const auto *cws = static_cast<const int4 *>(cws_buf);
619 GrottoSplitLeafBitsWork<input_bits, frontier_depth, block_root_depth, Dpf, Prg, Cws>(
620 party, s0, Cws{cws, packed_tr, stride}, leaves, prg);
621}
622
623// Device-key counterpart: the party seed and the packed control are loaded
624// from caller device storage; the shared traversal is unchanged.
625template <int input_bits, int frontier_depth, int block_root_depth, typename Dpf, typename Prg,
626 typename Cws>
627__global__ void GrottoSplitLeafBitsDeviceKeyKernel(bool party, const int4 *s0,
628 const void *cws_buf, const typename Cws::Control *tr, size_t stride, bool *leaves, Prg prg) {
629 const auto *cws = static_cast<const int4 *>(cws_buf);
630 GrottoSplitLeafBitsWork<input_bits, frontier_depth, block_root_depth, Dpf, Prg, Cws>(
631 party, s0[0], Cws{cws, tr[0], stride}, leaves, prg);
632}
633
634
635static __global__ void GrottoParityLevelKernel(bool *tree, size_t count) {
636 size_t i = static_cast<size_t>(blockIdx.x) * blockDim.x + threadIdx.x;
637 if (i >= count) return;
638 size_t index = count - 1 + i;
639 tree[index] = tree[2 * index + 1] ^ tree[2 * index + 2];
640}
641
642template <int top_level>
643__global__ void GrottoParityTopKernel(bool *tree) {
644#pragma unroll
645 for (int level = top_level; level >= 0; --level) {
646 int count = 1 << level;
647 if (threadIdx.x < count) {
648 int index = count - 1 + threadIdx.x;
649 tree[index] = tree[2 * index + 1] ^ tree[2 * index + 2];
650 }
651 __syncthreads();
652 }
653}
654
655static __global__ void GrottoPackedParityKernel(const int4 *blocks, bool *leaves, size_t count) {
656 size_t i = static_cast<size_t>(blockIdx.x) * blockDim.x + threadIdx.x;
657 if (i >= count) return;
658 int4 block = blocks[i];
659 unsigned value = static_cast<unsigned>(block.x) ^ static_cast<unsigned>(block.y) ^
660 static_cast<unsigned>(block.z) ^ static_cast<unsigned>(block.w);
661 leaves[i] = (__popc(value) & 1) != 0;
662}
663
664template <int leaf_bits, typename LaunchShape>
665inline cudaError_t BuildGrottoParityTree(bool *tree, cudaStream_t stream) {
666 constexpr int kThreads = LaunchShape::kThreadsPerBlock;
667 constexpr int kTop = LaunchShape::kThreadsPerBlockLog2 < leaf_bits - 1
668 ? LaunchShape::kThreadsPerBlockLog2 : leaf_bits - 1;
669 for (int level = leaf_bits - 1; level > kTop; --level) {
670 size_t count = size_t{1} << level;
671 size_t grid = count / kThreads + (count % kThreads != 0);
672 GrottoParityLevelKernel<<<grid, kThreads, 0, stream>>>(tree, count);
673 cudaError_t status = cudaGetLastError();
674 if (status != cudaSuccess) return status;
675 }
676 if constexpr (leaf_bits > 0) {
677 GrottoParityTopKernel<kTop><<<1, kThreads, 0, stream>>>(tree);
678 return cudaGetLastError();
679 }
680 return cudaSuccess;
681}
682
683#endif
684
685} // namespace gpu::detail
686
687template <int in_bits, typename Prg, typename In>
689__host__ __device__ inline GrottoDcf<in_bits, Prg, In>::GrottoDcf(Prg prg)
690 : prg_(prg) {}
691
692template <int in_bits, typename Prg, typename In>
694__host__ __device__ inline void GrottoDcf<in_bits, Prg, In>::Gen(cuda::std::span<int4> cws,
695 In &tr, cuda::std::span<const int4, 2> s0s, In a, size_t stride) {
696 // The key is the underlying DPF key, so the split layout is Dpf's and the
697 // underlying generator owns the stride, span and control-bit packing guards.
698 DpfType{prg_}.Gen(cws, tr, s0s, a, int4{0, 0, 0, 0}, stride);
699}
700
701template <int in_bits, typename Prg, typename In>
703__host__ __device__ inline bool GrottoDcf<in_bits, Prg, In>::Eval(bool b, int4 s0,
704 cuda::std::span<const int4> cws, In tr, In x, size_t stride) {
705 util::RequireArg(stride > 0, "GrottoDcf::Eval: stride must be positive");
706 constexpr size_t kMaxElements = SIZE_MAX / sizeof(int4);
707 util::RequireArg(stride <= (kMaxElements - 1) / static_cast<size_t>(in_bits),
708 "GrottoDcf::Eval: stride exceeds the correction array limit");
709 const size_t required = static_cast<size_t>(in_bits) * stride + 1;
710 util::RequireArg(cws.size() >= required,
711 "GrottoDcf::Eval: cws must hold in_bits*stride+1 elements");
712 return EvalCws(b, s0, CwsView{cws.data(), tr, stride}, x);
713}
714
715template <int in_bits, typename Prg, typename In>
717template <typename Cws>
718__host__ __device__ inline bool GrottoDcf<in_bits, Prg, In>::EvalCws(
719 bool b, int4 s0, Cws cws, In x) {
720 detail::GrottoTurns turns;
721 int4 leaf = DpfType{prg_}.Walk(b, s0, cws, x, turns);
722 return turns.PointLeaf(util::GetLsb(leaf));
723}
724
725template <int in_bits, typename Prg, typename In>
726 requires(ValidForInputBits<In, in_bits> && Prgable<Prg, 2>)
727inline void GrottoDcf<in_bits, Prg, In>::Preprocess(bool b, int4 s0,
728 cuda::std::span<const int4> cws, In tr, bool p[], int par_depth, size_t stride) {
729 if (stride == 0) throw std::invalid_argument("GrottoDcf::Preprocess: stride must be positive");
730 constexpr size_t kMaxElements = SIZE_MAX / sizeof(int4);
731 if (stride > (kMaxElements - 1) / static_cast<size_t>(in_bits))
732 throw std::invalid_argument("GrottoDcf::Preprocess: stride exceeds the correction array limit");
733 const size_t required = static_cast<size_t>(in_bits) * stride + 1;
734 if (cws.size() < required)
735 throw std::invalid_argument("GrottoDcf::Preprocess: cws must hold in_bits*stride+1 elements");
736 PreprocessCws(b, s0, CwsView{cws.data(), tr, stride}, p, par_depth);
737}
738
739template <int in_bits, typename Prg, typename In>
741template <typename Cws>
742inline void GrottoDcf<in_bits, Prg, In>::PreprocessCws(
743 bool b, int4 s0, Cws cws, bool p[], int par_depth) {
744 assert(par_depth >= 0);
745 static_assert(detail::FitsBinaryTree<bool, in_bits>(),
746 "parity-tree count and byte size must fit in size_t");
747 constexpr size_t n = detail::FullDomainSize<bool, in_bits>();
748 ExpandTree(b, s0, cws, p + n - 1, par_depth);
749 for (size_t j = n - 2; j < n - 1; --j) {
750 p[j] = p[2 * j + 1] ^ p[2 * j + 2];
751 }
752}
753
754template <int in_bits, typename Prg, typename In>
755 requires(ValidForInputBits<In, in_bits> && Prgable<Prg, 2>)
756__host__ __device__ inline bool GrottoDcf<in_bits, Prg, In>::Eval(ParityTreeView pt, In x) {
757 static_assert(detail::FitsBinaryTree<bool, in_bits>(),
758 "parity-tree count and byte size must fit in size_t");
759 constexpr size_t N = detail::FullDomainSize<bool, in_bits>();
760 In e = static_cast<In>(x) + 1;
761
762 // e == 0 means x + 1 overflowed, i.e., e = N (entire domain)
763 if (e == 0 || e == N) return pt.p[0];
764
765 bool pi = false;
766 size_t cur = 0;
767 for (int i = 0; i < in_bits; ++i) {
768 bool e_bit = (e >> (in_bits - 1 - i)) & 1;
769 if (e_bit) {
770 pi ^= pt.p[2 * cur + 1];
771 cur = 2 * cur + 2;
772 } else {
773 cur = 2 * cur + 1;
774 }
775 }
776 return pi;
777}
778
779template <int in_bits, typename Prg, typename In>
782 static_assert(detail::FitsBinaryTree<bool, in_bits>(),
783 "parity-tree count and byte size must fit in size_t");
784 constexpr size_t n = detail::FullDomainSize<bool, in_bits>();
785 bool prefix = false;
786 for (size_t x = 0; x < n; ++x) {
787 prefix ^= pt.p[n - 1 + x];
788 ys[x] = prefix;
789 }
790}
791
792template <int in_bits, typename Prg, typename In>
794inline void GrottoDcf<in_bits, Prg, In>::EvalAll(bool b, int4 s0,
795 cuda::std::span<const int4> cws, In tr, bool ys[], int par_depth, size_t stride) {
796 if (stride == 0) throw std::invalid_argument("GrottoDcf::EvalAll: stride must be positive");
797 constexpr size_t kMaxElements = SIZE_MAX / sizeof(int4);
798 if (stride > (kMaxElements - 1) / static_cast<size_t>(in_bits))
799 throw std::invalid_argument("GrottoDcf::EvalAll: stride exceeds the correction array limit");
800 const size_t required = static_cast<size_t>(in_bits) * stride + 1;
801 if (cws.size() < required)
802 throw std::invalid_argument("GrottoDcf::EvalAll: cws must hold in_bits*stride+1 elements");
803 EvalAllCws(b, s0, CwsView{cws.data(), tr, stride}, ys, par_depth);
804}
805
806template <int in_bits, typename Prg, typename In>
808template <typename Cws>
809inline void GrottoDcf<in_bits, Prg, In>::EvalAllCws(
810 bool b, int4 s0, Cws cws, bool ys[], int par_depth) {
811 assert(par_depth >= 0);
812 constexpr size_t N = detail::FullDomainSize<bool, in_bits>();
813
814 // Phase 1: expand tree to get leaf control bits into ys[]
815 ExpandTree(b, s0, cws, ys, par_depth);
816
817 // Phase 2b: prefix-sum scan (running XOR)
818 // ys[x] currently holds leaf x's control bit.
819 // Transform to: ys[x] = XOR of control bits [0..x] = share of 1[alpha <= x].
820 for (size_t x = 1; x < N; ++x) {
821 ys[x] = ys[x] ^ ys[x - 1];
822 }
823}
824
825template <int in_bits, typename Prg, typename In>
826 requires(ValidForInputBits<In, in_bits> && Prgable<Prg, 2>)
827template <typename Cws>
828inline void GrottoDcf<in_bits, Prg, In>::ExpandTree(
829 bool b, int4 s0, Cws cws, bool t[], int par_depth) {
830 int4 st = s0;
831 st = util::SetLsb(st, b);
832
833 size_t l = 0;
834 constexpr size_t r = detail::FullDomainSize<bool, in_bits>();
835 int i = 0;
836
837 int par_depth_ = fss::detail::ResolveParDepth(par_depth, in_bits);
838
839 if (par_depth_ == 0) {
840 ExpandTreeRec(st, cws, t, l, r, i, 0);
841 return;
842 }
843 int threads = fss::detail::ResolveParThreads(par_depth_, r);
844#pragma omp parallel num_threads(threads)
845#pragma omp single
846 ExpandTreeRec(st, cws, t, l, r, i, par_depth_);
847}
848
849template <int in_bits, typename Prg, typename In>
850 requires(ValidForInputBits<In, in_bits> && Prgable<Prg, 2>)
851template <typename Cws>
852inline void GrottoDcf<in_bits, Prg, In>::ExpandTreeRec(
853 int4 st, Cws cws, bool t[], size_t l, size_t r, int i, int par_depth_) {
854 bool tc = util::GetLsb(st);
855 int4 s = st;
856 s = util::SetLsb(s, false);
857
858 if (i == in_bits) {
859 assert(l + 1 == r);
860 t[l] = tc;
861 return;
862 }
863
864 const Cw cw = cws[i];
865 int4 s_cw = cw.s;
866 bool tl_cw = util::GetLsb(s_cw);
867 s_cw = util::SetLsb(s_cw, false);
868 bool tr_cw = cw.tr;
869
870 auto [sl, sr] = prg_.Gen(s);
871
872 bool tl = util::GetLsb(sl);
873 sl = util::SetLsb(sl, false);
874 bool tr = util::GetLsb(sr);
875 sr = util::SetLsb(sr, false);
876
877 if (tc) {
878 sl = util::Xor(sl, s_cw);
879 sr = util::Xor(sr, s_cw);
880 tl = tl ^ tl_cw;
881 tr = tr ^ tr_cw;
882 }
883
884 int4 stl = sl;
885 stl = util::SetLsb(stl, tl);
886 int4 str = sr;
887 str = util::SetLsb(str, tr);
888
889 size_t mid = (l + r) / 2;
890
891 if (i < par_depth_) {
892#pragma omp task
893 ExpandTreeRec(stl, cws, t, l, mid, i + 1, par_depth_);
894#pragma omp task
895 ExpandTreeRec(str, cws, t, mid, r, i + 1, par_depth_);
896#pragma omp taskwait
897 } else {
898 ExpandTreeRec(stl, cws, t, l, mid, i + 1, par_depth_);
899 ExpandTreeRec(str, cws, t, mid, r, i + 1, par_depth_);
900 }
901}
902
903template <int in_bits, typename Prg, typename In>
904 requires(ValidForInputBits<In, in_bits> && Prgable<Prg, 2>)
905template <int frontier_depth, int threads_per_block>
907 bool b, int4 s0, cuda::std::span<const int4> cws, In tr,
908 cuda::std::span<bool> tree, cudaStream_t stream, size_t stride) const
909 requires(in_bits < sizeof(size_t) * 8) {
910#if defined(__CUDACC__)
911 using LaunchShape = gpu::detail::GrottoGpuLaunchShape<in_bits, frontier_depth, threads_per_block>;
912 constexpr size_t kLeaves = size_t{1} << in_bits;
913 constexpr size_t kTreeSize = kLeaves + (kLeaves - 1);
914 constexpr size_t kMaxElements = std::numeric_limits<size_t>::max() / sizeof(int4);
915 if (!detail::FitsBinaryTree<bool, in_bits>() || stride == 0 ||
916 (stride > (kMaxElements - 1) / static_cast<size_t>(in_bits)) ||
917 cws.size() < static_cast<size_t>(in_bits) * stride + 1 ||
918 tree.size() < kTreeSize || cws.data() == nullptr || tree.data() == nullptr)
919 return cudaErrorInvalidValue;
920 constexpr size_t kParityWidth = kLeaves / 2;
921 constexpr size_t kParityBlocks = kParityWidth / LaunchShape::kThreadsPerBlock +
922 (kParityWidth % LaunchShape::kThreadsPerBlock != 0);
923 if (kParityBlocks > static_cast<size_t>(std::numeric_limits<int>::max()))
924 return cudaErrorInvalidConfiguration;
925 gpu::detail::GrottoSplitLeafBitsKernel<in_bits, LaunchShape::kFrontierDepth,
926 LaunchShape::kBlockRootDepth, DpfType, Prg, CwsView>
927 <<<LaunchShape::kBlocks, LaunchShape::kThreadsPerBlock, 0, stream>>>(
928 b, s0, cws.data(), tr, stride, tree.data() + kLeaves - 1, prg_);
929 cudaError_t status = cudaGetLastError();
930 if (status != cudaSuccess) return status;
931 return gpu::detail::BuildGrottoParityTree<in_bits, LaunchShape>(tree.data(), stream);
932#else
933 static_assert(sizeof(Prg) == 0, "gpu launch requires a CUDA compiler");
934 return cudaErrorNotSupported;
935#endif
936}
937
938template <int in_bits, typename Prg, typename In>
940template <int frontier_depth, int threads_per_block>
941__host__ inline cudaError_t GrottoDcf<in_bits, Prg, In>::EvalAllGpu(
942 bool b, int4 s0, cuda::std::span<const int4> cws, In tr,
943 cuda::std::span<bool> ys, cudaStream_t stream, size_t stride) const
944 requires(in_bits < sizeof(size_t) * 8) {
945#if defined(__CUDACC__)
946 using LaunchShape = gpu::detail::GrottoGpuLaunchShape<in_bits, frontier_depth, threads_per_block>;
947 constexpr size_t kOutputs = size_t{1} << in_bits;
948 constexpr size_t kMaxElements = std::numeric_limits<size_t>::max() / sizeof(int4);
949 if (!detail::FitsFullDomain<bool, in_bits>() || stride == 0 ||
950 (stride > (kMaxElements - 1) / static_cast<size_t>(in_bits)) ||
951 kOutputs > static_cast<size_t>(std::numeric_limits<int64_t>::max()) ||
952 cws.size() < static_cast<size_t>(in_bits) * stride + 1 || ys.size() < kOutputs ||
953 cws.data() == nullptr || ys.data() == nullptr) return cudaErrorInvalidValue;
954 size_t bytes = 0;
955 cudaError_t status = cub::DeviceScan::InclusiveScan(nullptr, bytes,
956 ys.data(), ys.data(), gpu::detail::GrottoGpuBoolXor{}, static_cast<int64_t>(kOutputs), stream);
957 if (status != cudaSuccess) return status;
958 gpu::detail::DeviceScratch scan_storage(stream);
959 status = scan_storage.Allocate<unsigned char>(bytes == 0 ? 1 : bytes);
960 if (status != cudaSuccess) return status;
961 gpu::detail::GrottoSplitLeafBitsKernel<in_bits, LaunchShape::kFrontierDepth,
962 LaunchShape::kBlockRootDepth, DpfType, Prg, CwsView>
963 <<<LaunchShape::kBlocks, LaunchShape::kThreadsPerBlock, 0, stream>>>(
964 b, s0, cws.data(), tr, stride, ys.data(), prg_);
965 status = cudaGetLastError();
966 if (status != cudaSuccess) return status;
967 status = cub::DeviceScan::InclusiveScan(scan_storage.Get<unsigned char>(), bytes,
968 ys.data(), ys.data(), gpu::detail::GrottoGpuBoolXor{}, static_cast<int64_t>(kOutputs), stream);
969 return gpu::detail::ReleaseScratch(status, {&scan_storage});
970#else
971 static_assert(sizeof(Prg) == 0, "gpu launch requires a CUDA compiler");
972 return cudaErrorNotSupported;
973#endif
974}
975
976template <int in_bits, typename Prg, typename In>
978__host__ inline cudaError_t GrottoDcf<in_bits, Prg, In>::EvalAllGpu(
979 ParityTreeView pt, cuda::std::span<bool> ys, cudaStream_t stream)
980 requires(in_bits < sizeof(size_t) * 8) {
981#if defined(__CUDACC__)
982 constexpr size_t kOutputs = size_t{1} << in_bits;
983 if (!detail::FitsBinaryTree<bool, in_bits>() ||
984 kOutputs > static_cast<size_t>(std::numeric_limits<int64_t>::max()) ||
985 ys.size() < kOutputs || ys.data() == nullptr || pt.p == nullptr) return cudaErrorInvalidValue;
986 const bool *leaves = pt.p + kOutputs - 1;
987 size_t bytes = 0;
988 cudaError_t status = cub::DeviceScan::InclusiveScan(nullptr, bytes,
989 leaves, ys.data(), gpu::detail::GrottoGpuBoolXor{}, static_cast<int64_t>(kOutputs), stream);
990 if (status != cudaSuccess) return status;
991 gpu::detail::DeviceScratch scan_storage(stream);
992 status = scan_storage.Allocate<unsigned char>(bytes == 0 ? 1 : bytes);
993 if (status != cudaSuccess) return status;
994 status = cub::DeviceScan::InclusiveScan(scan_storage.Get<unsigned char>(), bytes,
995 leaves, ys.data(), gpu::detail::GrottoGpuBoolXor{}, static_cast<int64_t>(kOutputs), stream);
996 return gpu::detail::ReleaseScratch(status, {&scan_storage});
997#else
998 static_assert(sizeof(Prg) == 0, "gpu launch requires a CUDA compiler");
999 return cudaErrorNotSupported;
1000#endif
1001}
1002
1003template <typename Scheme>
1004inline OwnedParityTree<Scheme>::OwnedParityTree(std::unique_ptr<bool[]> p)
1005 : p_(std::move(p)) {}
1006
1007template <typename Scheme>
1008inline typename Scheme::ParityTreeView OwnedParityTree<Scheme>::View() const & {
1009 return {p_.get()};
1010}
1011
1012template <int in_bits, typename Prg, typename In>
1015 bool b, int4 s0, cuda::std::span<const int4> cws, In tr, int par_depth, size_t stride) {
1016 if (stride == 0)
1017 throw std::invalid_argument("GrottoDcf::PreprocessOwned: stride must be positive");
1018 constexpr size_t kMaxElements = SIZE_MAX / sizeof(int4);
1019 if (stride > (kMaxElements - 1) / static_cast<size_t>(in_bits))
1020 throw std::invalid_argument("GrottoDcf::PreprocessOwned: stride exceeds the correction array limit");
1021 const size_t required = static_cast<size_t>(in_bits) * stride + 1;
1022 if (cws.size() < required)
1023 throw std::invalid_argument("GrottoDcf::PreprocessOwned: cws must hold in_bits*stride+1 elements");
1024 return PreprocessOwnedCws(b, s0,
1025 CwsView{cws.data(), tr, stride}, par_depth);
1026}
1027
1028template <int in_bits, typename Prg, typename In>
1030template <typename Cws>
1032GrottoDcf<in_bits, Prg, In>::PreprocessOwnedCws(
1033 bool b, int4 s0, Cws cws, int par_depth) {
1034 constexpr size_t tree_size = detail::FullBinaryTreeSize<bool, in_bits>();
1035 auto p = std::make_unique<bool[]>(tree_size);
1036 PreprocessCws(b, s0, cws, p.get(), par_depth);
1037 return OwnedParityTree<GrottoDcf<in_bits, Prg, In>>(std::move(p));
1038}
1039
1040namespace gpu::detail {
1041
1042#if defined(__CUDACC__)
1043
1044// Point-evaluate in_bits-level Grotto DCF keys: one thread per key, one
1045// single-path walk with in_bits PRG calls and no preprocessing. ys[k] is the
1046// party's share of 1[alpha <= xs[k]]. cws holds dense level-major correction
1047// words, the layout the split form of Gen() writes: layer i of key k is
1048// cws[i * nkeys + k]. tr[k] carries key k's packed right-control bits, bit i
1049// for layer i.
1050template <int in_bits, int unroll, bool b, typename Prg, typename In, typename Query>
1051__global__ void GrottoDcfEvalPointKernel(bool *ys, const int4 *seeds, const int4 *cws, const In *tr,
1052 const Query *xs, int nkeys, Prg prg) {
1053 static_assert(in_bits <= sizeof(In) * 8, "In too narrow for in_bits");
1054 int tid = blockIdx.x * blockDim.x + threadIdx.x;
1055 if (tid >= nkeys) return;
1056 int4 s = fss::util::SetLsb(seeds[tid], false);
1057 bool t = b;
1058 In x = static_cast<In>(xs[tid]);
1059 In tr_k = tr[tid];
1060 fss::detail::GrottoTurns turns;
1061
1062#pragma unroll unroll
1063 for (int i = 0; i < in_bits; ++i) {
1064 bool x_bit = GetBit(x, in_bits - 1 - i);
1065 turns(t, x_bit);
1066
1067 int4 s_cw = cws[(size_t)i * nkeys + tid];
1068 bool tr_cw = GetBit(tr_k, i);
1069 bool tl_cw = fss::util::GetLsb(s_cw);
1070 s_cw = fss::util::SetLsb(s_cw, false);
1071
1072 auto g = prg.Gen(s);
1073 int4 sl = g[0], sr = g[1];
1074 bool tl = fss::util::GetLsb(sl);
1075 sl = fss::util::SetLsb(sl, false);
1076 bool tr = fss::util::GetLsb(sr);
1077 sr = fss::util::SetLsb(sr, false);
1078
1079 if (t) {
1080 sl = fss::util::Xor(sl, s_cw);
1081 sr = fss::util::Xor(sr, s_cw);
1082 tl = tl ^ tl_cw;
1083 tr = tr ^ tr_cw;
1084 }
1085
1086 if (x_bit) {
1087 s = sr;
1088 t = tr;
1089 } else {
1090 s = sl;
1091 t = tl;
1092 }
1093 }
1094
1095 ys[tid] = turns.PointLeaf(t);
1096}
1097
1098template <int unroll, int in_bits, typename Prg, typename In, typename Query>
1099void GrottoDcfEvalPointLaunch(bool b, const int4 *seeds, const int4 *cws, const In *tr, const Query *xs,
1100 bool *ys, int nkeys, Prg prg, cudaStream_t stream) {
1101 if (nkeys == 0) return;
1102 constexpr int kBs = 256;
1103 int blocks = (nkeys + kBs - 1) / kBs;
1104 if (b) {
1105 GrottoDcfEvalPointKernel<in_bits, unroll, true, Prg, In, Query>
1106 <<<blocks, kBs, 0, stream>>>(ys, seeds, cws, tr, xs, nkeys, prg);
1107 } else {
1108 GrottoDcfEvalPointKernel<in_bits, unroll, false, Prg, In, Query>
1109 <<<blocks, kBs, 0, stream>>>(ys, seeds, cws, tr, xs, nkeys, prg);
1110 }
1111}
1112
1131template <int unroll = 4, int in_bits, typename Prg, typename In, typename Query = In>
1132 requires QueryFor<Query, in_bits>
1133void GrottoDcfEvalPointGpu(bool b, cuda::std::span<const int4> seeds, cuda::std::span<const int4> cws,
1134 cuda::std::span<const In> tr, cuda::std::span<const Query> xs, bool *ys, int nkeys,
1135 const GrottoDcf<in_bits, Prg, In> &dcf, cudaStream_t stream = 0) {
1136 if (nkeys < 0)
1137 throw std::invalid_argument("GrottoDcfEvalPointGpu: nkeys must be nonnegative");
1138 if (nkeys == 0) return;
1139 const size_t count = static_cast<size_t>(nkeys);
1140 constexpr size_t kLevels = static_cast<size_t>(in_bits) + 1;
1141 if (count > SIZE_MAX / kLevels || cws.size() < count * kLevels)
1142 throw std::invalid_argument("GrottoDcfEvalPointGpu: cws must hold (in_bits+1)*nkeys elements");
1143 if (seeds.size() < count || tr.size() < count || xs.size() < count || ys == nullptr)
1144 throw std::invalid_argument("GrottoDcfEvalPointGpu: seeds, tr, xs, and ys must cover nkeys entries");
1145 GrottoDcfEvalPointLaunch<unroll, in_bits, Prg, In, Query>(b, seeds.data(), cws.data(), tr.data(), xs.data(), ys,
1146 nkeys, fss::detail::SchemeAccess::GetPrg(dcf), stream);
1147}
1148
1149// Host launchers for the device-key kernels. The Prg arrives by value from a
1150// host mirror of the device scheme, matching the public launch methods.
1151template <int in_bits, typename Prg, typename In, int frontier_depth = 16,
1152 int threads_per_block = 256>
1153__host__ inline cudaError_t GrottoPreprocessGpuDeviceKey(bool b, const int4 *s0,
1154 const int4 *cws, size_t cws_count, const In *tr, size_t stride, bool *tree,
1155 size_t tree_count, const Prg &prg, cudaStream_t stream) {
1156 using LaunchShape = GrottoGpuLaunchShape<in_bits, frontier_depth, threads_per_block>;
1157 using DpfType = fss::Dpf<in_bits, fss::group::Bytes, Prg, In>;
1158 using CwsView = fss::detail::GrottoDcfCwsView<typename DpfType::Cw, In>;
1159 constexpr size_t kLeaves = size_t{1} << in_bits;
1160 constexpr size_t kTreeSize = kLeaves + (kLeaves - 1);
1161 constexpr size_t kMaxElements = std::numeric_limits<size_t>::max() / sizeof(int4);
1162 if (!fss::detail::FitsBinaryTree<bool, in_bits>() || stride == 0 ||
1163 stride > (kMaxElements - 1) / static_cast<size_t>(in_bits) ||
1164 cws_count < static_cast<size_t>(in_bits) * stride + 1 ||
1165 tree_count < kTreeSize || s0 == nullptr || cws == nullptr || tr == nullptr ||
1166 tree == nullptr) return cudaErrorInvalidValue;
1167 constexpr size_t kParityWidth = kLeaves / 2;
1168 constexpr size_t kParityBlocks = kParityWidth / LaunchShape::kThreadsPerBlock +
1169 (kParityWidth % LaunchShape::kThreadsPerBlock != 0);
1170 if (kParityBlocks > static_cast<size_t>(std::numeric_limits<int>::max()))
1171 return cudaErrorInvalidConfiguration;
1172 GrottoSplitLeafBitsDeviceKeyKernel<in_bits, LaunchShape::kFrontierDepth,
1173 LaunchShape::kBlockRootDepth, DpfType, Prg, CwsView>
1174 <<<LaunchShape::kBlocks, LaunchShape::kThreadsPerBlock, 0, stream>>>(
1175 b, s0, cws, tr, stride, tree + kLeaves - 1, prg);
1176 cudaError_t status = cudaGetLastError();
1177 if (status != cudaSuccess) return status;
1178 return BuildGrottoParityTree<in_bits, LaunchShape>(tree, stream);
1179}
1180
1181template <int in_bits, typename Prg, typename In, int frontier_depth = 16,
1182 int threads_per_block = 256>
1183__host__ inline cudaError_t GrottoEvalAllGpuDeviceKey(bool b, const int4 *s0,
1184 const int4 *cws, size_t cws_count, const In *tr, size_t stride, bool *ys,
1185 size_t ys_count, const Prg &prg, cudaStream_t stream) {
1186 using LaunchShape = GrottoGpuLaunchShape<in_bits, frontier_depth, threads_per_block>;
1187 using DpfType = fss::Dpf<in_bits, fss::group::Bytes, Prg, In>;
1188 using CwsView = fss::detail::GrottoDcfCwsView<typename DpfType::Cw, In>;
1189 constexpr size_t kOutputs = size_t{1} << in_bits;
1190 constexpr size_t kMaxElements = std::numeric_limits<size_t>::max() / sizeof(int4);
1191 if (!fss::detail::FitsFullDomain<bool, in_bits>() || stride == 0 ||
1192 stride > (kMaxElements - 1) / static_cast<size_t>(in_bits) ||
1193 kOutputs > static_cast<size_t>(std::numeric_limits<int64_t>::max()) ||
1194 cws_count < static_cast<size_t>(in_bits) * stride + 1 || ys_count < kOutputs ||
1195 s0 == nullptr || cws == nullptr || tr == nullptr || ys == nullptr)
1196 return cudaErrorInvalidValue;
1197 size_t bytes = 0;
1198 cudaError_t status = cub::DeviceScan::InclusiveScan(nullptr, bytes,
1199 ys, ys, gpu::detail::GrottoGpuBoolXor{}, static_cast<int64_t>(kOutputs), stream);
1200 if (status != cudaSuccess) return status;
1201 DeviceScratch scan_storage(stream);
1202 status = scan_storage.Allocate<unsigned char>(bytes == 0 ? 1 : bytes);
1203 if (status != cudaSuccess) return status;
1204 GrottoSplitLeafBitsDeviceKeyKernel<in_bits, LaunchShape::kFrontierDepth,
1205 LaunchShape::kBlockRootDepth, DpfType, Prg, CwsView>
1206 <<<LaunchShape::kBlocks, LaunchShape::kThreadsPerBlock, 0, stream>>>(
1207 b, s0, cws, tr, stride, ys, prg);
1208 status = cudaGetLastError();
1209 if (status != cudaSuccess) return status;
1210 status = cub::DeviceScan::InclusiveScan(scan_storage.Get<unsigned char>(), bytes,
1211 ys, ys, gpu::detail::GrottoGpuBoolXor{}, static_cast<int64_t>(kOutputs), stream);
1212 return ReleaseScratch(status, {&scan_storage});
1213}
1214
1215#endif // defined(__CUDACC__)
1216
1217} // namespace gpu::detail
1218
1219#if FSS_ENABLE_V1_COMPAT
1220
1221// Deprecated v1.3.0 aggregate-Cw entries. Every wrapper copies between the
1222// caller's Cw layout and a compile-time-sized split-component scratch buffer,
1223// then runs the current public method at stride 1. The Cw layout is the
1224// underlying DPF's, and the key keeps beta equal to zero.
1225template <int in_bits, typename Prg, typename In>
1226 requires(ValidForInputBits<In, in_bits> && Prgable<Prg, 2>)
1227__host__ __device__ inline void GrottoDcf<in_bits, Prg, In>::Gen(
1228 Cw cws[], const int4 s0s[2], In a) {
1229 int4 seed[in_bits + 1];
1230 In tr;
1231 Gen(cuda::std::span<int4>(seed, in_bits + 1), tr,
1232 cuda::std::span<const int4, 2>(s0s, 2), a);
1233 for (int i = 0; i < in_bits; ++i) cws[i] = {seed[i], ((tr >> i) & 1) != 0};
1234 cws[in_bits] = {seed[in_bits], false};
1235}
1236
1237template <int in_bits, typename Prg, typename In>
1238 requires(ValidForInputBits<In, in_bits> && Prgable<Prg, 2>)
1240 compat::detail::ParityTreeRecord<GrottoDcf> &pt, int4 s0, const Cw cws[]) {
1241 int4 seed[in_bits + 1];
1242 In tr = 0;
1243 for (int i = 0; i <= in_bits; ++i) seed[i] = cws[i].s;
1244 for (int i = 0; i < in_bits; ++i) tr |= static_cast<In>(cws[i].tr) << i;
1245 Preprocess(pt.b, s0, cuda::std::span<const int4>(seed, in_bits + 1), tr, pt.p);
1246}
1247
1248// Deprecated v1.3.0 aggregate-Key entries. Same Cw conversion as above; the
1249// Key entries additionally spread the shared correction words over both keys
1250// and take the party seeds from s0s. The owner allocates exactly the parity
1251// tree and fills it through the current caller-owned Preprocess; no hidden
1252// device copy exists. The old Eval() never read the party tag, so the query
1253// goes through ParityTreeView directly.
1254template <int in_bits, typename Prg, typename In>
1255 requires(ValidForInputBits<In, in_bits> && Prgable<Prg, 2>)
1256__host__ __device__ inline void GrottoDcf<in_bits, Prg, In>::Gen(
1257 compat::detail::CwsKeyRecord<GrottoDcf, Cw, in_bits + 1> &k0,
1258 compat::detail::CwsKeyRecord<GrottoDcf, Cw, in_bits + 1> &k1,
1259 cuda::std::span<const int4, 2> s0s, In a) {
1260 int4 seed[in_bits + 1];
1261 In tr;
1262 Gen(cuda::std::span<int4>(seed, in_bits + 1), tr, s0s, a);
1263 for (int i = 0; i < in_bits; ++i) {
1264 const Cw layer = {seed[i], ((tr >> i) & 1) != 0};
1265 k0.cws[i] = layer;
1266 k1.cws[i] = layer;
1267 }
1268 const Cw terminal = {seed[in_bits], false};
1269 k0.cws[in_bits] = terminal;
1270 k1.cws[in_bits] = terminal;
1271 k0.s0 = s0s[0];
1272 k1.s0 = s0s[1];
1273}
1274
1275template <int in_bits, typename Prg, typename In>
1276 requires(ValidForInputBits<In, in_bits> && Prgable<Prg, 2>)
1277inline compat::detail::PreprocessedTreeRecord<GrottoDcf<in_bits, Prg, In>>
1279 bool b, const compat::detail::CwsKeyRecord<GrottoDcf, Cw, in_bits + 1> &key) {
1280 int4 seed[in_bits + 1];
1281 In tr = 0;
1282 for (int i = 0; i <= in_bits; ++i) seed[i] = key.cws[i].s;
1283 for (int i = 0; i < in_bits; ++i) tr |= static_cast<In>(key.cws[i].tr) << i;
1284
1285 compat::detail::PreprocessedTreeRecord<GrottoDcf> pt;
1286 constexpr size_t kTreeSize = (size_t{2} << in_bits) - 1;
1287 pt.p = std::make_unique<bool[]>(kTreeSize);
1288 pt.b = b;
1289 Preprocess(b, key.s0, cuda::std::span<const int4>(seed, in_bits + 1), tr, pt.p.get());
1290 return pt;
1291}
1292
1293template <int in_bits, typename Prg, typename In>
1294 requires(ValidForInputBits<In, in_bits> && Prgable<Prg, 2>)
1295__host__ __device__ inline bool GrottoDcf<in_bits, Prg, In>::Eval(
1296 const compat::detail::ParityTreeRecord<GrottoDcf> &pt, In x) {
1297 return Eval(ParityTreeView{pt.p}, x);
1298}
1299
1300template <int in_bits, typename Prg, typename In>
1301 requires(ValidForInputBits<In, in_bits> && Prgable<Prg, 2>)
1303 const compat::detail::PreprocessedTreeRecord<GrottoDcf> &tree, In x) {
1304 return Eval(ParityTreeView{tree.p.get()}, x);
1305}
1306
1307template <int in_bits, typename Prg, typename In>
1308 requires(ValidForInputBits<In, in_bits> && Prgable<Prg, 2>)
1310 bool b, int4 s0, const Cw cws[], bool ys[]) {
1311 int4 seed[in_bits + 1];
1312 In tr = 0;
1313 for (int i = 0; i <= in_bits; ++i) seed[i] = cws[i].s;
1314 for (int i = 0; i < in_bits; ++i) tr |= static_cast<In>(cws[i].tr) << i;
1315 EvalAll(b, s0, cuda::std::span<const int4>(seed, in_bits + 1), tr, ys);
1316}
1317
1318template <int in_bits, typename Prg, typename In>
1319 requires(ValidForInputBits<In, in_bits> && Prgable<Prg, 2>)
1321 bool b, const compat::detail::CwsKeyRecord<GrottoDcf, Cw, in_bits + 1> &key,
1322 cuda::std::span<bool> ys) {
1323 int4 seed[in_bits + 1];
1324 In tr = 0;
1325 for (int i = 0; i <= in_bits; ++i) seed[i] = key.cws[i].s;
1326 for (int i = 0; i < in_bits; ++i) tr |= static_cast<In>(key.cws[i].tr) << i;
1327 EvalAll(b, key.s0, cuda::std::span<const int4>(seed, in_bits + 1), tr, ys.data());
1328}
1329
1330#endif // FSS_ENABLE_V1_COMPAT
1331
1332} // namespace fss
2-party DPF scheme.
Definition dpf.cuh:112
2-party DCF scheme over F2 from standard DPF (Grotto construction).
Definition grotto_dcf.cuh:103
bool Eval(bool b, int4 s0, cuda::std::span< const int4 > cws, In tr, In x, size_t stride=1)
Evaluate a comparison share along x's path from split correction words.
Definition grotto_dcf.cuh:703
In Input
The caller-facing input-domain type.
Definition grotto_dcf.cuh:112
GrottoDcf(Prg prg)
Configure the pseudorandom generator.
Definition grotto_dcf.cuh:689
void Gen(cuda::std::span< int4 > cws, In &tr, cuda::std::span< const int4, 2 > s0s, In a, size_t stride=1)
Generate split DPF correction words with beta equal to zero.
Definition grotto_dcf.cuh:694
cudaError_t EvalAllGpu(bool b, int4 s0, cuda::std::span< const int4 > cws, In tr, cuda::std::span< bool > ys, cudaStream_t stream=0, size_t stride=1) const
Evaluate all comparison shares on the GPU from split device correction words.
Definition grotto_dcf.cuh:941
cudaError_t PreprocessGpu(bool b, int4 s0, cuda::std::span< const int4 > cws, In tr, cuda::std::span< bool > tree, cudaStream_t stream=0, size_t stride=1) const
Build a parity tree on the GPU from split device correction words.
Definition grotto_dcf.cuh:906
static void EvalAll(ParityTreeView pt, bool ys[])
Evaluate the full domain from a host parity tree without PRG calls.
Definition grotto_dcf.cuh:781
OwnedParityTree< GrottoDcf > PreprocessOwned(bool b, int4 s0, cuda::std::span< const int4 > cws, In tr, int par_depth=0, size_t stride=1)
Allocate and populate a parity tree from split correction words in host storage.
Definition grotto_dcf.cuh:1014
static constexpr int kCwCount
Seed-component count Gen() writes at stride 1: in_bits layers plus the terminal.
Definition grotto_dcf.cuh:110
void Preprocess(bool b, int4 s0, cuda::std::span< const int4 > cws, In tr, bool p[], int par_depth=0, size_t stride=1)
Expand split leaf control bits and build a parity tree in caller-owned host storage.
Definition grotto_dcf.cuh:727
typename DpfType::Cw Cw
Correction-word type of the underlying DPF.
Definition grotto_dcf.cuh:107
Own a host parity-tree allocation and expose a borrowed, read-only view.
Definition grotto_dcf.cuh:480
Scheme::ParityTreeView View() const &
Return a view borrowing this live owner's host allocation.
Definition grotto_dcf.cuh:1008
Scheme::ParityTreeView View() const &&=delete
Reject a temporary owner whose borrowed view would immediately dangle.
OwnedParityTree(std::unique_ptr< bool[]> p)
Take ownership of an already populated host parity tree.
Definition grotto_dcf.cuh:1004
Single build-time switch for the deprecated v1 Cw-form compatibility entries.
Pseudorandom generator (PRG) interface.
Definition prg.cuh:24
Unsigned input storage wide enough for an input domain of in_bits bits.
Definition constraints.cuh:29
2-party distributed point function (DPF).
bool GetLsb(int4 val)
Return bit 0 of the fourth word of val.
Definition util.cuh:58
void RequireArg(bool ok, const char *msg)
Check a caller precondition.
Definition util.cuh:73
int4 SetLsb(int4 val, bool bit)
Return val with bit 0 of its fourth word set to bit.
Definition util.cuh:51
int4 Xor(int4 lhs, int4 rhs)
Return the bitwise XOR of two 128-bit blocks.
Definition util.cuh:29
Borrowed, read-only parity tree in level order.
Definition grotto_dcf.cuh:189
const bool * p
Borrowed parity entries, including leaves.
Definition grotto_dcf.cuh:190