40#include <cuda_runtime.h>
48#include <cuda/std/array>
52#include <cuda/std/span>
76template <
typename Cw,
typename In,
int in_bits>
82 __host__ __device__
inline Cw operator[](
int i)
const {
83 const int4 s_cw = s[
static_cast<size_t>(i) * stride];
84 if (i >= in_bits)
return Cw{s_cw,
false};
85 return Cw{s_cw, ((packed_tr >> i) & 1) != 0};
91template <
int in_bits,
typename Prg,
typename In>
109template <
int in_bits,
typename Group,
typename Prg,
typename In = u
int>
111 Groupable<Group> && Prgable<Prg, 2>)
123 struct __align__(32)
Cw {
128 static_assert(
sizeof(
Cw) == 32);
140#if FSS_ENABLE_V1_COMPAT
144 [[deprecated(
"the par_depth template parameter is gone; pass par_depth to EvalAll() instead")]]
145 static constexpr int kParDepth = -1;
159 __host__ __device__
explicit Dpf(Prg prg);
187 __host__ __device__
void Gen(cuda::std::span<int4> cws, In &tr,
188 cuda::std::span<const int4, 2> s0s, In a, int4 b_buf,
size_t stride = 1);
217 __host__ __device__ int4
Eval(
bool b, int4 s0, cuda::std::span<const int4> cws,
218 In tr, In x,
size_t stride = 1);
252 void EvalRange(
bool b, In first, In last, int4 s0, cuda::std::span<const int4> cws,
253 In tr, cuda::std::span<int4> ys,
int par_depth = 0,
size_t stride = 1);
283 void EvalAll(
bool b, int4 s0, cuda::std::span<const int4> cws, In tr,
284 int4 ys[],
int par_depth = 0,
size_t stride = 1);
313 template <
int frontier_depth = 16,
int threads_per_block = 256>
315 cuda::std::span<const int4> cws, In tr, cuda::std::span<int4> ys,
316 cudaStream_t stream = 0,
size_t stride = 1)
const;
341 template <
int frontier_depth = 16,
int threads_per_block = 256>
342 __host__ cudaError_t
EvalAllGpu(
bool b, int4 s0, cuda::std::span<const int4> cws,
343 In tr, cuda::std::span<int4> ys, cudaStream_t stream = 0,
size_t stride = 1) const
344 requires(in_bits < sizeof(
size_t) * 8);
378 template <
int frontier_depth = 16,
int threads_per_block = 256>
380 cuda::std::span<const int4> cws, cuda::std::span<const In> tr,
381 cuda::std::span<int4> ys, cudaStream_t stream = 0,
size_t stride = 0) const
382 requires(in_bits < sizeof(
size_t) * 8);
384#if FSS_ENABLE_V1_COMPAT
390 [[deprecated(
"use the split-component Gen(cuda::std::span<int4>, In&, cuda::std::span<const int4, 2>, In, int4, size_t) overload")]]
391 __host__ __device__
void Gen(Cw cws[],
const int4 s0s[2], In a, int4 b_buf);
394 [[deprecated(
"use the split-component Eval(bool, int4, cuda::std::span<const int4>, In, In, size_t) overload")]]
395 __host__ __device__ int4
Eval(
bool b, int4 s0,
const Cw cws[], In x);
398 [[deprecated(
"use the split-component EvalAll(bool, int4, cuda::std::span<const int4>, In, int4[], int, size_t) overload")]]
399 void EvalAll(
bool b, int4 s0,
const Cw cws[], int4 ys[]);
408 [[deprecated(
"use the split-component Gen(cuda::std::span<int4>, In&, cuda::std::span<const int4, 2>, In, int4, size_t) overload")]]
409 __host__ __device__
void Gen(Key &k0, Key &k1, cuda::std::span<const int4, 2> s0s,
413 [[deprecated(
"use the split-component Eval(bool, int4, cuda::std::span<const int4>, In, In, size_t) overload")]]
414 __host__ __device__ int4
Eval(
bool b,
const Key &key, In x);
417 [[deprecated(
"use the split-component EvalAll(bool, int4, cuda::std::span<const int4>, In, int4[], int, size_t) overload")]]
418 void EvalAll(
bool b,
const Key &key, cuda::std::span<int4> ys);
422 friend class detail::SchemeAccess;
426 template <
int frontier_depth,
int threads_per_block>
427 struct GpuLaunchShape;
433 static constexpr int kBatchPrefix = 3;
434 static constexpr int kBatchTail = 3;
436 friend class GrottoDcf<in_bits, Prg, In>;
442 __host__ __device__
static void ExpandNode(Prg &prg, int4 state, int4 correction,
443 bool right_control, int4 &left, int4 &right);
445 __host__ __device__
static int4 PathStep(Prg &prg, int4 state, int4 correction,
446 bool right_control,
bool take_right);
450 template <
typename Correction>
451 __host__ __device__
static int4 ConvertLeaf(
bool party, int4 state, Correction &correction);
455 using CwsView = detail::DpfCwsView<Cw, In, in_bits>;
461 __host__ __device__
void Write(
int i, int4 s_cw,
bool tr_cw)
const {
466 int4 *cw_slot =
reinterpret_cast<int4 *
>(&cws[i]);
468 cw_slot[1] = make_int4(tr_cw, 0, 0, 0);
471 __host__ __device__
void Finish(int4 terminal)
const {
472 cws[in_bits] = {terminal,
false};
483 __host__ __device__
void Write(
int i, int4 s_cw,
bool tr_cw) {
484 cws[
static_cast<size_t>(i) * stride] = s_cw;
485 packed_tr |=
static_cast<In
>(tr_cw) << i;
488 __host__ __device__
void Finish(int4 terminal) {
489 cws[
static_cast<size_t>(in_bits) * stride] = terminal;
496 template <
typename Writer>
497 __host__ __device__
void Generate(Writer writer,
const int4 *s0s, In a, int4 b_buf);
501 template <
typename Cws,
typename Visit>
502 __host__ __device__ int4 Walk(
bool b, int4 s0, Cws cws, In x, Visit &&visit);
505 template <
typename Cws>
506 __host__ __device__ int4 EvalCws(
bool b, int4 s0, Cws cws, In x);
509 template <
typename Cws>
510 void EvalRangeCws(
bool b, In first, In last, int4 s0, Cws cws,
511 cuda::std::span<int4> ys,
int par_depth);
514 template <
typename Cws>
515 void EvalAllCws(
bool b, int4 s0, Cws cws, int4 ys[],
int par_depth);
520 template <
int frontier_depth,
int threads_per_block>
521 __host__ cudaError_t EvalAllGpuBatchCw(
bool b, cuda::std::span<const int4> s0s,
522 cuda::std::span<const Cw> cws, cuda::std::span<int4> ys, cudaStream_t stream)
const;
525 template <
typename Cws>
526 void EvalRangeSerial(
bool b, In first, In last, int4 s0, Cws cws, int4 ys[]);
531 template <
typename Cws>
532#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ == 12 && __GNUC_MINOR__ <= 4
533 __attribute__((noipa))
535 std::vector<int4> BuildParallelFrontier(int4 st, Cws cws,
int split_depth);
540 template <
typename Cws>
541 void ExpandFrontier(int4 frontier[],
size_t w, Cws cws,
int lv);
547 __host__ __device__
static int4 CorrectChild(int4 child, int4 s_cw,
int m,
int t_cw);
553 template <
typename Cws>
554 void EvalBatched8(
bool b, int4 st, Cws cws, int4 ys[],
size_t l,
int i);
558 template <
typename Cws>
559 void EvalBatched8Group(
bool b,
const int4 (&frontier)[8], Cws cws, int4 ys[],
size_t l,
int i);
564namespace gpu::detail {
566template <
typename Scheme>
567struct DpfGpuSchemeTraits;
569#if defined(__CUDACC__)
575template <
typename Scheme,
int frontier_depth,
int block_root_depth,
bool range = false,
typename Cws>
576__device__ __forceinline__
void DpfEvalAllBlock(
bool b, int4 s0, Cws cws,
577 int4 *ys,
typename DpfGpuSchemeTraits<Scheme>::Backend &prg,
int subtree_block_index, int64_t thread_index, int4 *shared_frontier,
578 __uint128_t first = 0,
int subtree_depth = DpfGpuSchemeTraits<Scheme>::kInputBits,
579 int range_frontier = frontier_depth,
int range_block_root = block_root_depth) {
580 constexpr int kInputBits = DpfGpuSchemeTraits<Scheme>::kInputBits;
581 constexpr int kTailCapacity = range ? 8 : kInputBits - frontier_depth;
582 const int start_depth = range ? kInputBits - subtree_depth : 0;
583 const int frontier = range ? start_depth + range_frontier : frontier_depth;
584 const int root_depth = range ? start_depth + range_block_root : block_root_depth;
585 const int tail_depth = kInputBits - frontier;
586 const int block_subtree_depth = frontier - root_depth;
588 if (threadIdx.x == 0) {
591 for (
int i = 0; i < root_depth; ++i) {
592 bool x_bit = i < start_depth
593 ? ((first >> (kInputBits - 1 - i)) & 1) != 0
594 : ((subtree_block_index >> (root_depth - 1 - i)) & 1) != 0;
598 st = fss::detail::SchemeAccess::DpfPathStep<Scheme>(node_prg, st, cws[i].s, cws[i].tr, x_bit);
601 shared_frontier[0] = st;
607 for (
int i = 0; i < block_subtree_depth; ++i) {
608 const bool active = threadIdx.x < (1 << (i + 1));
609 int4 n = {0, 0, 0, 0};
610 if (active) n = shared_frontier[threadIdx.x >> 1];
618 fss::detail::SchemeAccess::DpfExpandNode<Scheme>(node_prg, n, cws[root_depth + i].s, cws[root_depth + i].tr, left, right);
620 shared_frontier[threadIdx.x] = (threadIdx.x & 1) ? right : left;
625 if constexpr (range) {
627 int4 node = shared_frontier[threadIdx.x];
628 int level = frontier;
629 const int count = 1 << tail_depth;
630 const size_t offset =
static_cast<size_t>(thread_index) << tail_depth;
631 for (
int index = 0; index < count; ++index) {
632 while (level < kInputBits) {
636 fss::detail::SchemeAccess::DpfExpandNode<Scheme>(node_prg, node,
637 cws[level].s, cws[level].tr, left, right);
639 pending[level - frontier] = right;
643 ys[offset + index] = fss::detail::SchemeAccess::DpfConvertLeaf<Scheme>(
644 b, node, cws[kInputBits].s);
645 if (index + 1 < count) {
646 int next = index + 1;
647 level = kInputBits - 1;
648 while ((next & 1) == 0) {
652 node = pending[level - frontier];
658 int4 nodes[1 << kTailCapacity];
659 nodes[0] = shared_frontier[threadIdx.x];
662 for (
int lev = frontier; lev < kInputBits; ++lev) {
664 for (
int k = count - 1; k >= 0; --k) {
669 fss::detail::SchemeAccess::DpfExpandNode<Scheme>(node_prg, nodes[k], cws[lev].s, cws[lev].tr, left, right);
672 nodes[2 * k + 1] = right;
677 int4 v_cw_np1 = cws[kInputBits].s;
678 const size_t output_offset =
static_cast<size_t>(thread_index) << tail_depth;
680 for (
int k = 0; k < count; ++k) {
681 ys[output_offset + k] = fss::detail::SchemeAccess::DpfConvertLeaf<Scheme>(b, nodes[k], v_cw_np1);
686template <
typename Scheme,
int frontier_depth,
int block_root_depth>
687__global__
void DpfEvalAllBatchKernel(
bool b,
const int4 *s0s,
const void *cws_buf,
688 int4 *ys,
typename DpfGpuSchemeTraits<Scheme>::Backend prg) {
691 const auto *cws =
static_cast<const typename Scheme::Cw *
>(cws_buf);
692 constexpr int kInputBits = DpfGpuSchemeTraits<Scheme>::kInputBits;
693 constexpr int kBlocksPerKey = 1 << block_root_depth;
694 const int key_index = blockIdx.x / kBlocksPerKey;
695 const int subtree_block_index = blockIdx.x - key_index * kBlocksPerKey;
696 const int64_t thread_index =
static_cast<int64_t
>(subtree_block_index) * blockDim.x + threadIdx.x;
697 if (thread_index >= (1LL << frontier_depth))
return;
698 const auto *key_cws = cws +
static_cast<size_t>(key_index) * (kInputBits + 1);
699 int4 *key_outputs = ys +
static_cast<size_t>(key_index) * (
size_t{1} << kInputBits);
700 __shared__ int4 shared_frontier[1 << (frontier_depth - block_root_depth)];
701 DpfEvalAllBlock<Scheme, frontier_depth, block_root_depth>(b, s0s[key_index], key_cws, key_outputs, prg, subtree_block_index, thread_index, shared_frontier);
707template <
typename Scheme,
int threads_per_block>
708__global__
void DpfSplitEvalRangeKernel(
bool b, int4 s0,
709 const int4 *cws,
typename DpfGpuSchemeTraits<Scheme>::Input tr,
710 size_t stride, int4 *ys, __uint128_t first,
711 int depth,
int frontier,
int root,
typename DpfGpuSchemeTraits<Scheme>::Backend prg) {
712 using Cws = fss::detail::DpfCwsView<
typename Scheme::Cw,
713 typename DpfGpuSchemeTraits<Scheme>::Input,
714 DpfGpuSchemeTraits<Scheme>::kInputBits>;
715 __shared__ int4 states[threads_per_block];
716 const int64_t thread_index =
static_cast<int64_t
>(blockIdx.x) * blockDim.x + threadIdx.x;
717 DpfEvalAllBlock<Scheme, 0, 0, true>(b, s0, Cws{cws, tr, stride}, ys, prg,
718 blockIdx.x, thread_index, states, first, depth, frontier, root);
721template <
typename Scheme,
int frontier_depth,
int block_root_depth>
722__global__
void DpfSplitEvalAllKernel(
bool b, int4 s0,
const int4 *cws,
723 typename DpfGpuSchemeTraits<Scheme>::Input tr,
size_t stride,
724 int4 *ys,
typename DpfGpuSchemeTraits<Scheme>::Backend prg) {
725 using Cws = fss::detail::DpfCwsView<
typename Scheme::Cw,
726 typename DpfGpuSchemeTraits<Scheme>::Input,
727 DpfGpuSchemeTraits<Scheme>::kInputBits>;
728 const int64_t thread_index =
static_cast<int64_t
>(blockIdx.x) * blockDim.x + threadIdx.x;
729 if (thread_index >= (1LL << frontier_depth))
return;
730 __shared__ int4 shared_frontier[1 << (frontier_depth - block_root_depth)];
731 DpfEvalAllBlock<Scheme, frontier_depth, block_root_depth>(b, s0, Cws{cws, tr, stride},
732 ys, prg, blockIdx.x, thread_index, shared_frontier);
735template <
typename Scheme,
int frontier_depth,
int block_root_depth>
736__global__
void DpfSplitEvalAllBatchKernel(
bool b,
const int4 *s0s,
const int4 *cws,
737 const typename DpfGpuSchemeTraits<Scheme>::Input *trs,
size_t stride,
738 int4 *ys,
typename DpfGpuSchemeTraits<Scheme>::Backend prg) {
739 using Cws = fss::detail::DpfCwsView<
typename Scheme::Cw,
740 typename DpfGpuSchemeTraits<Scheme>::Input,
741 DpfGpuSchemeTraits<Scheme>::kInputBits>;
742 constexpr int kInputBits = DpfGpuSchemeTraits<Scheme>::kInputBits;
743 constexpr int kBlocksPerKey = 1 << block_root_depth;
744 const int key_index = blockIdx.x / kBlocksPerKey;
745 const int subtree_block_index = blockIdx.x - key_index * kBlocksPerKey;
746 const int64_t thread_index =
static_cast<int64_t
>(subtree_block_index) * blockDim.x + threadIdx.x;
747 if (thread_index >= (1LL << frontier_depth))
return;
748 int4 *key_outputs = ys +
static_cast<size_t>(key_index) * (
size_t{1} << kInputBits);
749 __shared__ int4 shared_frontier[1 << (frontier_depth - block_root_depth)];
750 DpfEvalAllBlock<Scheme, frontier_depth, block_root_depth>(b, s0s[key_index],
751 Cws{cws + key_index, trs[key_index], stride}, key_outputs, prg,
752 subtree_block_index, thread_index, shared_frontier);
758template <
typename Scheme,
int frontier_depth,
int block_root_depth>
759__global__
void DpfSplitEvalAllDeviceKeyKernel(
bool b,
const int4 *s0,
760 const int4 *cws,
const typename DpfGpuSchemeTraits<Scheme>::Input *tr,
761 size_t stride, int4 *ys,
typename DpfGpuSchemeTraits<Scheme>::Backend prg) {
762 using Cws = fss::detail::DpfCwsView<
typename Scheme::Cw,
763 typename DpfGpuSchemeTraits<Scheme>::Input,
764 DpfGpuSchemeTraits<Scheme>::kInputBits>;
765 const int64_t thread_index =
static_cast<int64_t
>(blockIdx.x) * blockDim.x + threadIdx.x;
766 if (thread_index >= (1LL << frontier_depth))
return;
767 __shared__ int4 shared_frontier[1 << (frontier_depth - block_root_depth)];
768 DpfEvalAllBlock<Scheme, frontier_depth, block_root_depth>(b, s0[0],
769 Cws{cws, tr[0], stride}, ys, prg, blockIdx.x, thread_index, shared_frontier);
772template <
typename Scheme,
int threads_per_block>
773__global__
void DpfSplitEvalRangeDeviceKeyKernel(
bool b,
const int4 *s0,
774 const int4 *cws,
const typename DpfGpuSchemeTraits<Scheme>::Input *tr,
775 size_t stride, int4 *ys, __uint128_t first,
776 int depth,
int frontier,
int root,
typename DpfGpuSchemeTraits<Scheme>::Backend prg) {
777 using Cws = fss::detail::DpfCwsView<
typename Scheme::Cw,
778 typename DpfGpuSchemeTraits<Scheme>::Input,
779 DpfGpuSchemeTraits<Scheme>::kInputBits>;
780 __shared__ int4 states[threads_per_block];
781 const int64_t thread_index =
static_cast<int64_t
>(blockIdx.x) * blockDim.x + threadIdx.x;
782 DpfEvalAllBlock<Scheme, 0, 0, true>(b, s0[0], Cws{cws, tr[0], stride}, ys, prg,
783 blockIdx.x, thread_index, states, first, depth, frontier, root);
789template <
typename Scheme,
int frontier_depth,
int block_root_depth>
790__global__
void DpfSplitEvalAllBatchDeviceKeyKernel(
bool b,
const int4 *s0s,
791 const int4 *cws,
size_t key_records,
size_t control_record,
792 size_t key_count, int4 *ys,
typename DpfGpuSchemeTraits<Scheme>::Backend prg) {
793 using In =
typename DpfGpuSchemeTraits<Scheme>::Input;
794 using Cws = fss::detail::DpfCwsView<
typename Scheme::Cw, In,
795 DpfGpuSchemeTraits<Scheme>::kInputBits>;
796 constexpr int kInputBits = DpfGpuSchemeTraits<Scheme>::kInputBits;
797 constexpr int kBlocksPerKey = 1 << block_root_depth;
798 const int key_index = blockIdx.x / kBlocksPerKey;
799 const int subtree_block_index = blockIdx.x - key_index * kBlocksPerKey;
800 const int64_t thread_index =
static_cast<int64_t
>(subtree_block_index) * blockDim.x + threadIdx.x;
801 if (thread_index >= (1LL << frontier_depth))
return;
802 const int4 *key = cws +
static_cast<size_t>(key_index) * key_records;
803 const In *tr =
reinterpret_cast<const In *
>(key + control_record);
804 int4 *key_outputs = ys +
static_cast<size_t>(key_index) * (
size_t{1} << kInputBits);
805 __shared__ int4 shared_frontier[1 << (frontier_depth - block_root_depth)];
806 DpfEvalAllBlock<Scheme, frontier_depth, block_root_depth>(b, s0s[key_index],
807 Cws{key, tr[0], 1}, key_outputs, prg, subtree_block_index, thread_index, shared_frontier);
812template <
int in_bits,
typename Group,
typename Prg,
typename In,
int frontier_depth = 16,
813 int threads_per_block = 256>
814__host__
inline cudaError_t DpfEvalAllGpuDeviceKey(
bool b,
const int4 *s0,
815 const int4 *cws,
size_t cws_count,
const In *tr,
size_t stride, int4 *ys,
size_t ys_count,
816 const Prg &prg, cudaStream_t stream) {
817 constexpr size_t kMaxElements = std::numeric_limits<size_t>::max() /
sizeof(int4);
818 constexpr size_t kOutputsPerKey =
size_t{1} << in_bits;
819 constexpr int kFrontierDepth = frontier_depth < in_bits ? frontier_depth : in_bits;
820 if (stride == 0 || stride > (kMaxElements - 1) /
static_cast<size_t>(in_bits) ||
821 cws_count <
static_cast<size_t>(in_bits) * stride + 1 || ys_count < kOutputsPerKey ||
822 s0 ==
nullptr || cws ==
nullptr || tr ==
nullptr || ys ==
nullptr ||
823 kOutputsPerKey > kMaxElements)
824 return cudaErrorInvalidValue;
825 static_assert(threads_per_block >= 1 && threads_per_block <= 1024 &&
826 (threads_per_block & (threads_per_block - 1)) == 0,
827 "gpu block size must be a power of two in [1,1024]");
828 static_assert(in_bits - kFrontierDepth <= 8,
829 "gpu frontier leaves more than eight tail levels; specify a larger frontier_depth");
830 constexpr int kThreadsPerBlockLog2 = [] {
831 int requested_log2 = 0;
832 for (
int threads = threads_per_block; threads > 1; threads >>= 1) ++requested_log2;
833 return requested_log2 < kFrontierDepth ? requested_log2 : kFrontierDepth;
835 constexpr int kBlockRootDepth = kFrontierDepth - kThreadsPerBlockLog2;
836 using Scheme = Dpf<in_bits, Group, Prg, In>;
837 DpfSplitEvalAllDeviceKeyKernel<Scheme, kFrontierDepth, kBlockRootDepth>
838 <<<
size_t{1} << kBlockRootDepth, 1 << kThreadsPerBlockLog2, 0, stream>>>(
839 b, s0, cws, tr, stride, ys, prg);
840 return cudaGetLastError();
843template <
int in_bits,
typename Group,
typename Prg,
typename In,
int frontier_depth = 16,
844 int threads_per_block = 256>
845__host__
inline cudaError_t DpfEvalRangeGpuDeviceKey(
bool b, In first, In last,
846 const int4 *s0,
const int4 *cws,
size_t cws_count,
const In *tr,
size_t stride,
847 int4 *ys,
size_t ys_count,
const Prg &prg, cudaStream_t stream) {
848 constexpr size_t kMaxElements = std::numeric_limits<size_t>::max() /
sizeof(int4);
849 if (stride == 0 || stride > (kMaxElements - 1) /
static_cast<size_t>(in_bits) ||
850 cws_count <
static_cast<size_t>(in_bits) * stride + 1 || cws ==
nullptr ||
851 s0 ==
nullptr || tr ==
nullptr || ys ==
nullptr)
return cudaErrorInvalidValue;
852 using Scheme = Dpf<in_bits, Group, Prg, In>;
853 return fss::detail::SplitRangeChunks<in_bits, frontier_depth, threads_per_block>(
854 first, last, ys_count,
855 [&](In chunk_first,
int depth,
size_t offset,
int frontier,
int thread_bits,
int root,
857 DpfSplitEvalRangeDeviceKeyKernel<Scheme, threads_per_block>
858 <<<blocks, 1 << thread_bits, 0, stream>>>(b, s0, cws, tr, stride,
859 ys + offset, chunk_first, depth, frontier, root, prg);
860 return cudaGetLastError();
864template <
int in_bits,
typename Group,
typename Prg,
typename In,
int frontier_depth = 16,
865 int threads_per_block = 256>
866__host__
inline cudaError_t DpfEvalAllGpuBatchDeviceKey(
bool b,
const int4 *s0s,
867 size_t key_count,
const int4 *cws,
size_t cws_count,
size_t key_records,
868 size_t control_record, int4 *ys,
size_t ys_count,
const Prg &prg, cudaStream_t stream) {
869 constexpr size_t kMaxElements = std::numeric_limits<size_t>::max() /
sizeof(int4);
870 constexpr size_t kOutputsPerKey =
size_t{1} << in_bits;
871 constexpr size_t kMaxAddressableSize = std::numeric_limits<size_t>::max();
872 constexpr int kFrontierDepth = frontier_depth < in_bits ? frontier_depth : in_bits;
873 static_assert(threads_per_block >= 1 && threads_per_block <= 1024 &&
874 (threads_per_block & (threads_per_block - 1)) == 0,
875 "gpu block size must be a power of two in [1,1024]");
876 static_assert(in_bits - kFrontierDepth <= 8,
877 "gpu frontier leaves more than eight tail levels; specify a larger frontier_depth");
878 constexpr int kThreadsPerBlockLog2 = [] {
879 int requested_log2 = 0;
880 for (
int threads = threads_per_block; threads > 1; threads >>= 1) ++requested_log2;
881 return requested_log2 < kFrontierDepth ? requested_log2 : kFrontierDepth;
883 constexpr int kBlockRootDepth = kFrontierDepth - kThreadsPerBlockLog2;
884 constexpr int kBlocksPerKey = 1 << kBlockRootDepth;
885 if (key_count == 0)
return cudaSuccess;
886 if (key_count > kMaxAddressableSize / kOutputsPerKey /
sizeof(int4) ||
887 key_count > kMaxAddressableSize / key_records /
sizeof(int4) ||
888 cws_count < key_count * key_records || ys_count < key_count * kOutputsPerKey ||
889 s0s ==
nullptr || cws ==
nullptr || ys ==
nullptr ||
890 kOutputsPerKey > kMaxElements)
return cudaErrorInvalidValue;
891 if (key_count >
static_cast<size_t>(std::numeric_limits<int>::max()) / kBlocksPerKey)
892 return cudaErrorInvalidConfiguration;
893 using Scheme = Dpf<in_bits, Group, Prg, In>;
894 DpfSplitEvalAllBatchDeviceKeyKernel<Scheme, kFrontierDepth, kBlockRootDepth>
895 <<<key_count * kBlocksPerKey, 1 << kThreadsPerBlockLog2, 0, stream>>>(
896 b, s0s, cws, key_records, control_record, key_count, ys, prg);
897 return cudaGetLastError();
904template <
typename Scheme,
int unroll,
bool b,
typename Control,
typename Query>
905__global__
void DpfCoreEvalPointKernel(int4 *ys,
const int4 *seeds,
const int4 *cws,
906 const Control *tr,
const int4 *out_cw,
907 const Query *xs,
int key_count,
908 typename DpfGpuSchemeTraits<Scheme>::Backend prg) {
909 using In =
typename DpfGpuSchemeTraits<Scheme>::Input;
910 constexpr int kInputBits = DpfGpuSchemeTraits<Scheme>::kInputBits;
911 static_assert(kInputBits <=
sizeof(In) * 8,
"In too narrow for kInputBits");
912 int thread_index = blockIdx.x * blockDim.x + threadIdx.x;
913 if (thread_index >= key_count)
return;
915 In x =
static_cast<In
>(xs[thread_index]);
916 Control ex = tr[thread_index];
918 for (
int i = 0; i < kInputBits; ++i) {
919 bool take_right = ((x >> (kInputBits - 1 - i)) & 1) != 0;
920 bool right_control = ((ex >> i) & 1) != 0;
921 state = fss::detail::SchemeAccess::DpfPathStep<Scheme>(
922 prg, state, cws[
static_cast<size_t>(i) * key_count + thread_index], right_control, take_right);
924 ys[thread_index] = fss::detail::SchemeAccess::DpfConvertLeaf<Scheme>(b, state, out_cw[thread_index]);
927template <
typename Scheme,
typename Control,
typename Query>
928struct DpfPointGpuLauncher;
930template <
int n,
typename Group,
typename Prg,
typename In,
typename Control,
typename Query>
931struct DpfPointGpuLauncher<Dpf<n, Group, Prg, In>, Control, Query> {
932 template <
int unroll>
933 static void Launch(
bool b,
const int4 *seeds,
const int4 *cws,
const Control *tr,
934 const int4 *out_cw,
const Query *xs, int4 *ys,
int key_count, Prg prg,
935 cudaStream_t stream) {
936 using Scheme = Dpf<n, Group, Prg, In>;
937 constexpr int kThreadsPerBlock = 256;
938 int block_count = (key_count + kThreadsPerBlock - 1) / kThreadsPerBlock;
940 DpfCoreEvalPointKernel<Scheme, unroll, true, Control, Query>
941 <<<block_count, kThreadsPerBlock, 0, stream>>>(ys, seeds, cws, tr, out_cw, xs, key_count, prg);
943 DpfCoreEvalPointKernel<Scheme, unroll, false, Control, Query>
944 <<<block_count, kThreadsPerBlock, 0, stream>>>(ys, seeds, cws, tr, out_cw, xs, key_count, prg);
949template <
int unroll,
typename Scheme,
typename Control,
typename Query>
950void DpfCoreEvalPointLaunch(
bool b,
const int4 *seeds,
const int4 *cws,
const Control *tr,
951 const int4 *out_cw,
const Query *xs, int4 *ys,
int key_count,
952 typename DpfGpuSchemeTraits<Scheme>::Backend prg, cudaStream_t stream) {
953 if (key_count == 0)
return;
954 DpfPointGpuLauncher<Scheme, Control, Query>::template Launch<unroll>(
955 b, seeds, cws, tr, out_cw, xs, ys, key_count, prg, stream);
976template <
int unroll = 4,
int in_bits,
typename Group,
typename Prg,
typename In,
typename Query = In>
977 requires QueryFor<Query, in_bits>
978void DpfEvalPointGpu(
bool b, cuda::std::span<const int4> seeds, cuda::std::span<const int4> cws,
979 cuda::std::span<const In> tr, cuda::std::span<const Query> xs, int4 *ys,
int nkeys,
980 const Dpf<in_bits, Group, Prg, In> &dpf, cudaStream_t stream = 0) {
982 throw std::invalid_argument(
"DpfEvalPointGpu: nkeys must be nonnegative");
983 if (nkeys == 0)
return;
984 const size_t count =
static_cast<size_t>(nkeys);
985 constexpr size_t kLevels =
static_cast<size_t>(in_bits) + 1;
986 if (count > SIZE_MAX / kLevels || cws.size() < count * kLevels)
987 throw std::invalid_argument(
"DpfEvalPointGpu: cws must hold (in_bits+1)*nkeys elements");
988 if (seeds.size() < count || tr.size() < count || xs.size() < count || ys ==
nullptr)
989 throw std::invalid_argument(
"DpfEvalPointGpu: seeds, tr, xs, and ys must cover nkeys entries");
990 const int4 *out_cw = cws.data() +
static_cast<size_t>(in_bits) *
static_cast<size_t>(nkeys);
991 DpfCoreEvalPointLaunch<unroll, Dpf<in_bits, Group, Prg, In>, In, Query>(b, seeds.data(), cws.data(), tr.data(),
992 out_cw, xs.data(), ys, nkeys, fss::detail::SchemeAccess::GetPrg(dpf), stream);
999namespace gpu::detail {
1001template <
int n,
typename Group,
typename Prg,
typename In>
1002struct DpfGpuSchemeTraits<Dpf<n, Group, Prg, In>> {
1003 using Backend = Prg;
1005 static constexpr int kInputBits = n;
1010template <
int in_bits,
typename Group,
typename Prg,
typename In>
1012 Groupable<Group> && Prgable<Prg, 2>)
1016template <
int in_bits,
typename Group,
typename Prg,
typename In>
1020 cuda::std::span<int4> cws, In &tr, cuda::std::span<const int4, 2> s0s,
1021 In a, int4 b_buf,
size_t stride) {
1023 constexpr size_t kMaxElements = SIZE_MAX /
sizeof(int4);
1024 util::RequireArg(stride <= (kMaxElements - 1) /
static_cast<size_t>(in_bits),
1025 "Dpf::Gen: stride exceeds the correction array limit");
1026 const size_t required =
static_cast<size_t>(in_bits) * stride + 1;
1028 "Dpf::Gen: cws must hold in_bits*stride+1 elements");
1029 Generate(CwsWriter{cws.data(), stride, tr}, s0s.data(), a, b_buf);
1032template <
int in_bits,
typename Group,
typename Prg,
typename In>
1035template <typename Writer>
1036__host__ __device__
inline void Dpf<in_bits, Group, Prg, In>::Generate(
1037 Writer writer,
const int4 *s0s, In a, int4 b_buf) {
1046 for (
int i = 0; i < in_bits; ++i) {
1047 auto [s0l, s0r] = prg_.Gen(s0);
1048 auto [s1l, s1r] = prg_.Gen(s1);
1059 bool a_bit = (a >> (in_bits - 1 - i)) & 1;
1065 bool tl_cw = t0l ^ t1l ^ a_bit ^ 1;
1066 bool tr_cw = t0r ^ t1r ^ a_bit;
1074 if (t0) t0 = t0l ^ tl_cw;
1076 if (t1) t1 = t1l ^ tl_cw;
1084 if (t0) t0 = t0r ^ tr_cw;
1086 if (t1) t1 = t1r ^ tr_cw;
1091 writer.Write(i, s_cw, tr_cw);
1094 auto v_cw_np1 = Group::From(b_buf) + (-Group::From(s0)) + Group::From(s1);
1095 if (t1) v_cw_np1 = -v_cw_np1;
1096 writer.Finish(v_cw_np1.Into());
1099template <
int in_bits,
typename Group,
typename Prg,
typename In>
1101 Groupable<Group> && Prgable<Prg, 2>)
1103 cuda::std::span<const int4> cws, In tr, In x,
size_t stride) {
1105 constexpr size_t kMaxElements = SIZE_MAX /
sizeof(int4);
1106 util::RequireArg(stride <= (kMaxElements - 1) /
static_cast<size_t>(in_bits),
1107 "Dpf::Eval: stride exceeds the correction array limit");
1108 const size_t required =
static_cast<size_t>(in_bits) * stride + 1;
1110 "Dpf::Eval: cws must hold in_bits*stride+1 elements");
1111 return EvalCws(b, s0, CwsView{cws.data(), tr, stride}, x);
1114template <
int in_bits,
typename Group,
typename Prg,
typename In>
1117template <typename Cws>
1118__host__ __device__
inline int4 Dpf<in_bits, Group, Prg, In>::EvalCws(
1119 bool b, int4 s0, Cws cws, In x) {
1120 int4 state = Walk(b, s0, cws, x, [](
bool,
bool) {});
1121 const int4 terminal = cws[in_bits].s;
1123 "Dpf::Eval: the terminal group value must have a zero LSB");
1124 return ConvertLeaf(b, state, terminal);
1127template <
int in_bits,
typename Group,
typename Prg,
typename In>
1129 Groupable<Group> && Prgable<Prg, 2>)
1131 int4 s0, cuda::std::span<const int4> cws, In tr, cuda::std::span<int4> ys,
1132 int par_depth,
size_t stride) {
1133 if (stride == 0)
throw std::invalid_argument(
"Dpf::EvalRange: stride must be positive");
1134 constexpr size_t kMaxElements = SIZE_MAX /
sizeof(int4);
1135 if (stride > (kMaxElements - 1) /
static_cast<size_t>(in_bits))
1136 throw std::invalid_argument(
"Dpf::EvalRange: stride exceeds the correction array limit");
1137 const size_t required =
static_cast<size_t>(in_bits) * stride + 1;
1138 if (cws.size() < required || cws.data() ==
nullptr)
1139 throw std::invalid_argument(
"Dpf::EvalRange: cws must hold in_bits*stride+1 elements");
1140 EvalRangeCws(b, first, last, s0, CwsView{cws.data(), tr, stride},
1144template <
int in_bits,
typename Group,
typename Prg,
typename In>
1147template <typename Cws>
1148inline void Dpf<in_bits, Group, Prg, In>::EvalRangeCws(
bool b, In first, In last,
1149 int4 s0, Cws cws, cuda::std::span<int4> ys,
int par_depth) {
1150 assert(par_depth >= 0);
1152 throw std::invalid_argument(
"Dpf::EvalRange: first must not exceed last");
1153 if constexpr (in_bits <
sizeof(In) * 8) {
1154 if ((last >> in_bits) != 0)
1155 throw std::invalid_argument(
"Dpf::EvalRange: last must stay inside the input domain");
1157 const __uint128_t difference =
static_cast<__uint128_t
>(last) - first;
1158 if (difference >= std::numeric_limits<size_t>::max() /
sizeof(int4))
1159 throw std::invalid_argument(
"Dpf::EvalRange: the interval is too large for size_t");
1160 const size_t count =
static_cast<size_t>(difference) + 1;
1161 if (ys.size() < count || ys.data() ==
nullptr)
1162 throw std::invalid_argument(
"Dpf::EvalRange: ys must hold last-first+1 entries");
1164 "Dpf::EvalRange: the terminal group value must have a zero LSB");
1165 const size_t partitions = detail::ResolveParPartitions(par_depth, count);
1166 detail::ParallelFor(par_depth, partitions, [&](
size_t partition) {
1167 const size_t remainder = count % partitions;
1168 const size_t begin = partition * (count / partitions) +
1169 (partition < remainder ? partition : remainder);
1170 const size_t length = count / partitions + (partition < remainder);
1171 const In partition_first = first +
static_cast<In
>(begin);
1172 const In partition_last = partition_first +
static_cast<In
>(length - 1);
1173 EvalRangeSerial(b, partition_first, partition_last, s0, cws, ys.data() + begin);
1177template <
int in_bits,
typename Group,
typename Prg,
typename In>
1179 Groupable<Group> && Prgable<Prg, 2>)
1180template <typename Cws>
1181inline void Dpf<in_bits, Group, Prg, In>::EvalRangeSerial(
bool b, In first, In last,
1182 int4 s0, Cws cws, int4 ys[]) {
1183 constexpr In domain_last = [] {
1184 if constexpr (in_bits ==
sizeof(In) * 8)
return static_cast<In
>(~In{0});
1185 else return static_cast<In
>((In{1} << in_bits) - 1);
1193 cuda::std::array<Frame, in_bits + 1> stack;
1195 stack[0] = {
util::SetLsb(s0, b), In{0}, domain_last, 0};
1196 while (pending != 0) {
1197 const Frame node = stack[--pending];
1198 if (node.level == in_bits) {
1199 const int4 terminal = cws[in_bits].s;
1200 ys[
static_cast<size_t>(node.low - first)] = ConvertLeaf(b, node.state, terminal);
1203 const In middle = node.low + ((node.high - node.low) >> 1);
1206 ExpandNode(prg_, node.state, cws[node.level].s, cws[node.level].tr, left, right);
1207 if (last > middle) {
1208 assert(pending < stack.size());
1209 stack[pending++] = {right,
static_cast<In
>(middle + 1), node.high, node.level + 1};
1211 if (first <= middle) {
1212 assert(pending < stack.size());
1213 stack[pending++] = {left, node.low, middle, node.level + 1};
1218template <
int in_bits,
typename Group,
typename Prg,
typename In>
1220 Groupable<Group> && Prgable<Prg, 2>)
1222 cuda::std::span<const int4> cws, In tr, int4 ys[],
int par_depth,
size_t stride) {
1223 if (stride == 0)
throw std::invalid_argument(
"Dpf::EvalAll: stride must be positive");
1224 constexpr size_t kMaxElements = SIZE_MAX /
sizeof(int4);
1225 if (stride > (kMaxElements - 1) /
static_cast<size_t>(in_bits))
1226 throw std::invalid_argument(
"Dpf::EvalAll: stride exceeds the correction array limit");
1227 const size_t required =
static_cast<size_t>(in_bits) * stride + 1;
1228 if (cws.size() < required)
1229 throw std::invalid_argument(
"Dpf::EvalAll: cws must hold in_bits*stride+1 elements");
1230 EvalAllCws(b, s0, CwsView{cws.data(), tr, stride}, ys, par_depth);
1233template <
int in_bits,
typename Group,
typename Prg,
typename In>
1236template <typename Cws>
1237inline void Dpf<in_bits, Group, Prg, In>::EvalAllCws(
1238 bool b, int4 s0, Cws cws, int4 ys[],
int par_depth) {
1239 assert(par_depth >= 0);
1240 static_assert(detail::FitsFullDomain<int4, in_bits>(),
1241 "full-domain output count and byte size must fit in size_t");
1247 "Dpf::EvalAll: the terminal group value must have a zero LSB");
1249 if constexpr (in_bits < kBatchPrefix + kBatchTail) {
1255 constexpr size_t n =
size_t{1} << in_bits;
1256 const int4 terminal = cws[in_bits].s;
1257 fss::detail::ParallelFor(par_depth, n, [&](
size_t x) {
1258 const int4 state = Walk(b, s0, cws,
static_cast<In
>(x), [](
bool,
bool) {});
1259 ys[x] = ConvertLeaf(b, state, terminal);
1267 int par_depth_ = fss::detail::ResolveParDepth(par_depth, in_bits);
1269 int split_depth = par_depth_;
1270 if (split_depth > in_bits - kBatchPrefix - kBatchTail) split_depth = in_bits - kBatchPrefix - kBatchTail;
1271 if (split_depth > 0) {
1274 size_t num_tasks =
size_t{1} << split_depth;
1275 std::vector<int4> frontier = BuildParallelFrontier(st, cws, split_depth);
1276 size_t span = (
size_t{1} << in_bits) / num_tasks;
1277 fss::detail::ParallelFor(par_depth_, num_tasks, [&](
size_t task) {
1278 EvalBatched8(b, frontier[task], cws, ys, task * span, split_depth);
1281 EvalBatched8(b, st, cws, ys, 0, 0);
1285template <
int in_bits,
typename Group,
typename Prg,
typename In>
1287 Groupable<Group> && Prgable<Prg, 2>)
1288__host__ __device__
inline void Dpf<in_bits, Group, Prg, In>::ExpandNode(
1289 Prg &prg, int4 state, int4 correction,
bool right_control, int4 &left, int4 &right) {
1291 auto [raw_left, raw_right] = prg.Gen(
util::SetLsb(state,
false));
1292 int left_control =
static_cast<int>(
util::GetLsb(correction));
1293 int4 seed_correction =
util::SetLsb(correction,
false);
1294 left = CorrectChild(raw_left, seed_correction, -control, control & left_control);
1295 right = CorrectChild(raw_right, seed_correction, -control, control &
static_cast<int>(right_control));
1298template <
int in_bits,
typename Group,
typename Prg,
typename In>
1300 Groupable<Group> && Prgable<Prg, 2>)
1301__host__ __device__
inline int4 Dpf<in_bits, Group, Prg, In>::PathStep(
1302 Prg &prg, int4 state, int4 correction,
bool right_control,
bool take_right) {
1304 auto [raw_child] = detail::GenHalf<2>(prg,
util::SetLsb(state,
false), take_right);
1305 int child_control =
static_cast<int>(take_right ? right_control :
util::GetLsb(correction));
1306 return CorrectChild(raw_child,
util::SetLsb(correction,
false),
1307 -control, control & child_control);
1310template <
int in_bits,
typename Group,
typename Prg,
typename In>
1312 Groupable<Group> && Prgable<Prg, 2>)
1313template <typename Correction>
1314__host__ __device__
inline int4 Dpf<in_bits, Group, Prg, In>::ConvertLeaf(
1315 bool party, int4 state, Correction &correction) {
1318 if constexpr (std::is_same_v<std::remove_cv_t<Correction>, int4>) value = value + Group::From(correction);
1319 else value = value + correction;
1321 if (party) value = -value;
1322 return value.Into();
1325template <
int in_bits,
typename Group,
typename Prg,
typename In>
1327 Groupable<Group> && Prgable<Prg, 2>)
1328template <typename Cws, typename Visit>
1329__host__ __device__
inline int4 Dpf<in_bits, Group, Prg, In>::Walk(
1330 bool b, int4 s0, Cws cws, In x, Visit &&visit) {
1332 for (
int i = 0; i < in_bits; ++i) {
1333 bool x_bit = (x >> (in_bits - 1 - i)) & 1;
1335 state = PathStep(prg_, state, cws[i].s, cws[i].tr, x_bit);
1340template <
int in_bits,
typename Group,
typename Prg,
typename In>
1342 Groupable<Group> && Prgable<Prg, 2>)
1343template <typename Cws>
1344inline std::vector<int4> Dpf<in_bits, Group, Prg, In>::BuildParallelFrontier(
1345 int4 st, Cws cws,
int split_depth) {
1346 std::vector<int4> frontier(
size_t{1} << split_depth);
1348 for (
int lv = 0; lv < split_depth; ++lv) ExpandFrontier(frontier.data(),
size_t{1} << lv, cws, lv);
1352template <
int in_bits,
typename Group,
typename Prg,
typename In>
1354 Groupable<Group> && Prgable<Prg, 2>)
1355template <typename Cws>
1356inline void Dpf<in_bits, Group, Prg, In>::ExpandFrontier(int4 frontier[],
size_t w, Cws cws,
int lv) {
1357 int4 correction = cws[lv].s;
1358 bool right_control = cws[lv].tr;
1359 for (
size_t j = w; j-- > 0;) {
1361 ExpandNode(prg_, frontier[j], correction, right_control, left, right);
1362 frontier[2 * j] = left;
1363 frontier[2 * j + 1] = right;
1367template <
int in_bits,
typename Group,
typename Prg,
typename In>
1369 Groupable<Group> && Prgable<Prg, 2>)
1370__host__ __device__
inline int4 Dpf<in_bits, Group, Prg, In>::CorrectChild(int4 child, int4 s_cw,
int m,
int t_cw) {
1371 int4 res = {child.x ^ (s_cw.x & m), child.y ^ (s_cw.y & m), child.z ^ (s_cw.z & m), child.w ^ (s_cw.w & m)};
1372 res.w = (res.w & ~1) | ((child.w ^ t_cw) & 1);
1376template <
int in_bits,
typename Group,
typename Prg,
typename In>
1378 Groupable<Group> && Prgable<Prg, 2>)
1379template <typename Cws>
1380inline void Dpf<in_bits, Group, Prg, In>::EvalBatched8(
1381 bool b, int4 st, Cws cws, int4 ys[],
size_t l,
int i) {
1384 for (
int lv = i; lv < i + kBatchPrefix; ++lv) ExpandFrontier(frontier,
size_t{1} << (lv - i), cws, lv);
1385 EvalBatched8Group(b, frontier, cws, ys, l, i + kBatchPrefix);
1388template <
int in_bits,
typename Group,
typename Prg,
typename In>
1390 Groupable<Group> && Prgable<Prg, 2>)
1391template <typename Cws>
1392inline void Dpf<in_bits, Group, Prg, In>::EvalBatched8Group(
1393 bool b,
const int4 (&frontier)[8], Cws cws, int4 ys[],
size_t l,
int i) {
1394 if (i == in_bits - kBatchTail) {
1396 int4 bufs[2][8 << kBatchTail];
1397 int4 *cur = bufs[0];
1398 int4 *next = bufs[1];
1399 for (
int j = 0; j < 8; ++j) cur[j] = frontier[j];
1401 for (
int lv = i; lv < in_bits; ++lv) {
1404 int tr_cw = cws[lv].tr;
1405 for (
int g = 0; g < w / 8; ++g) {
1406 cuda::std::array<int4, 8> seeds;
1408 for (
int j = 0; j < 8; ++j) {
1409 ts[j] =
static_cast<int>(
util::GetLsb(cur[8 * g + j]));
1412 auto out = detail::GenBatch<2>(prg_, seeds);
1413 for (
int j = 0; j < 8; ++j) {
1415 next[16 * g + 2 * j] = CorrectChild(out[j][0], s_cw, m, ts[j] & tl_cw);
1416 next[16 * g + 2 * j + 1] = CorrectChild(out[j][1], s_cw, m, ts[j] & tr_cw);
1425 int4 v_cw_np1 = cws[in_bits].s;
1427 assert((v_cw_np1.w & 1) == 0);
1428 Group v_cw = Group::From(v_cw_np1);
1429 for (
int j = 0; j < w; ++j) ys[l + j] = ConvertLeaf(b, cur[j], v_cw);
1435 int tr_cw = cws[i].tr;
1437 cuda::std::array<int4, 8> seeds;
1439 for (
int j = 0; j < 8; ++j) {
1443 auto out = detail::GenBatch<2>(prg_, seeds);
1448 for (
int j = 0; j < 4; ++j) {
1450 low[2 * j] = CorrectChild(out[j][0], s_cw, m, ts[j] & tl_cw);
1451 low[2 * j + 1] = CorrectChild(out[j][1], s_cw, m, ts[j] & tr_cw);
1452 int m4 = -ts[j + 4];
1453 high[2 * j] = CorrectChild(out[j + 4][0], s_cw, m4, ts[j + 4] & tl_cw);
1454 high[2 * j + 1] = CorrectChild(out[j + 4][1], s_cw, m4, ts[j + 4] & tr_cw);
1457 size_t half =
size_t{8} << (in_bits - i - 1);
1458 EvalBatched8Group(b, low, cws, ys, l, i + 1);
1459 EvalBatched8Group(b, high, cws, ys, l + half, i + 1);
1462template <
int in_bits,
typename Group,
typename Prg,
typename In>
1464 Groupable<Group> && Prgable<Prg, 2>)
1465template <int frontier_depth, int threads_per_block>
1467struct Dpf<in_bits, Group, Prg, In>::GpuLaunchShape {
1468 static_assert(frontier_depth >= 0,
"gpu frontier depth must be nonnegative");
1469 static_assert(1 <= threads_per_block && threads_per_block <= 1024 && (threads_per_block & (threads_per_block - 1)) == 0,
1470 "gpu block size must be a power of two in [1,1024]");
1471 static constexpr int kFrontierDepth = frontier_depth < in_bits ? frontier_depth : in_bits;
1472 static_assert(in_bits - kFrontierDepth <= 8,
1473 "gpu frontier leaves more than eight tail levels; specify a larger frontier_depth");
1474 static constexpr int kThreadsPerBlockLog2 = [] {
1475 int threads_per_block_log2 = 0;
1476 for (
int thread_count = threads_per_block; thread_count > 1; thread_count >>= 1) ++threads_per_block_log2;
1477 return threads_per_block_log2 < kFrontierDepth ? threads_per_block_log2 : kFrontierDepth;
1479 static constexpr int kThreadsPerBlock = 1 << kThreadsPerBlockLog2;
1480 static constexpr int kBlockRootDepth = kFrontierDepth - kThreadsPerBlockLog2;
1481 static_assert(kBlockRootDepth <= 30,
"gpu block index exceeds the signed-int range");
1482 static constexpr size_t kBlocksPerKey =
size_t{1} << (kBlockRootDepth <= 30 ? kBlockRootDepth : 0);
1485template <
int in_bits,
typename Group,
typename Prg,
typename In>
1487 Groupable<Group> && Prgable<Prg, 2>)
1488template <int frontier_depth, int threads_per_block>
1489__host__
inline cudaError_t Dpf<in_bits, Group, Prg, In>::EvalAllGpuBatchCw(
1490 bool b, cuda::std::span<const int4> s0s, cuda::std::span<const Cw> cws,
1491 cuda::std::span<int4> ys, cudaStream_t stream)
const {
1492#if defined(__CUDACC__)
1493 using LaunchShape = GpuLaunchShape<frontier_depth, threads_per_block>;
1495 constexpr size_t kOutputsPerKey =
size_t{1} << in_bits;
1496 constexpr size_t kCwPerKey = in_bits + 1;
1497 constexpr size_t kMaxAddressableSize = std::numeric_limits<size_t>::max();
1498 size_t key_count = s0s.size();
1499 if (key_count == 0)
return cudaSuccess;
1500 if (key_count > kMaxAddressableSize / kCwPerKey /
sizeof(Cw) || key_count > kMaxAddressableSize / kOutputsPerKey /
sizeof(int4) ||
1501 cws.size() < key_count * kCwPerKey || ys.size() < key_count * kOutputsPerKey ||
1502 s0s.data() ==
nullptr || cws.data() ==
nullptr || ys.data() ==
nullptr)
return cudaErrorInvalidValue;
1503 if (key_count >
static_cast<size_t>(std::numeric_limits<int>::max()) / LaunchShape::kBlocksPerKey)
1504 return cudaErrorInvalidConfiguration;
1505 gpu::detail::DpfEvalAllBatchKernel<Scheme, LaunchShape::kFrontierDepth, LaunchShape::kBlockRootDepth>
1506 <<<key_count * LaunchShape::kBlocksPerKey, LaunchShape::kThreadsPerBlock, 0, stream>>>(
1507 b, s0s.data(), cws.data(), ys.data(), prg_);
1508 return cudaGetLastError();
1510 static_assert(
sizeof(Prg) == 0,
"gpu launch requires a CUDA compiler");
1511 return cudaErrorNotSupported;
1515template <
int in_bits,
typename Group,
typename Prg,
typename In>
1517 Groupable<Group> && Prgable<Prg, 2>)
1518template <int frontier_depth, int threads_per_block>
1520 bool b, In first, In last, int4 s0, cuda::std::span<const int4> cws, In tr,
1521 cuda::std::span<int4> ys, cudaStream_t stream,
size_t stride)
const {
1522#if defined(__CUDACC__)
1524 static_assert(frontier_depth >= 0,
"gpu frontier depth must be nonnegative");
1525 static_assert(threads_per_block >= 1 && threads_per_block <= 1024 &&
1526 (threads_per_block & (threads_per_block - 1)) == 0,
1527 "gpu block size must be a power of two in [1,1024]");
1528 constexpr size_t kMaxElements = std::numeric_limits<size_t>::max() /
sizeof(int4);
1529 if (stride == 0 || (stride > (kMaxElements - 1) /
static_cast<size_t>(in_bits)) ||
1530 cws.size() <
static_cast<size_t>(in_bits) * stride + 1 ||
1531 cws.data() ==
nullptr || ys.data() ==
nullptr)
1532 return cudaErrorInvalidValue;
1533 return detail::SplitRangeChunks<in_bits, frontier_depth, threads_per_block>(
1534 first, last, ys.size(),
1535 [&](In chunk_first,
int depth,
size_t offset,
int frontier,
int thread_bits,
int root,
1537 gpu::detail::DpfSplitEvalRangeKernel<Scheme, threads_per_block>
1538 <<<blocks, 1 << thread_bits, 0, stream>>>(b, s0, cws.data(), tr, stride,
1539 ys.data() + offset, chunk_first, depth, frontier, root, prg_);
1540 return cudaGetLastError();
1543 static_assert(
sizeof(Prg) == 0,
"gpu launch requires a CUDA compiler");
1544 return cudaErrorNotSupported;
1548template <
int in_bits,
typename Group,
typename Prg,
typename In>
1551template <int frontier_depth, int threads_per_block>
1553 bool b, int4 s0, cuda::std::span<const int4> cws, In tr,
1554 cuda::std::span<int4> ys, cudaStream_t stream,
size_t stride)
const
1555 requires(in_bits <
sizeof(size_t) * 8) {
1556#if defined(__CUDACC__)
1557 using LaunchShape = GpuLaunchShape<frontier_depth, threads_per_block>;
1559 constexpr size_t kOutputsPerKey =
size_t{1} << in_bits;
1560 constexpr size_t kMaxElements = std::numeric_limits<size_t>::max() /
sizeof(int4);
1562 (stride > (kMaxElements - 1) /
static_cast<size_t>(in_bits)))
1563 return cudaErrorInvalidValue;
1564 const size_t required =
static_cast<size_t>(in_bits) * stride + 1;
1565 if (cws.size() < required || ys.size() < kOutputsPerKey ||
1566 cws.data() ==
nullptr || ys.data() ==
nullptr ||
1567 kOutputsPerKey > std::numeric_limits<size_t>::max() /
sizeof(int4))
return cudaErrorInvalidValue;
1568 gpu::detail::DpfSplitEvalAllKernel<Scheme, LaunchShape::kFrontierDepth, LaunchShape::kBlockRootDepth>
1569 <<<LaunchShape::kBlocksPerKey, LaunchShape::kThreadsPerBlock, 0, stream>>>(
1570 b, s0, cws.data(), tr, stride, ys.data(), prg_);
1571 return cudaGetLastError();
1573 static_assert(
sizeof(Prg) == 0,
"gpu launch requires a CUDA compiler");
1574 return cudaErrorNotSupported;
1578template <
int in_bits,
typename Group,
typename Prg,
typename In>
1581template <int frontier_depth, int threads_per_block>
1583 bool b, cuda::std::span<const int4> s0s, cuda::std::span<const int4> cws,
1584 cuda::std::span<const In> tr, cuda::std::span<int4> ys,
1585 cudaStream_t stream,
size_t stride)
const
1586 requires(in_bits <
sizeof(size_t) * 8) {
1587#if defined(__CUDACC__)
1588 using LaunchShape = GpuLaunchShape<frontier_depth, threads_per_block>;
1590 constexpr size_t kOutputsPerKey =
size_t{1} << in_bits;
1591 constexpr size_t kMaxElements = std::numeric_limits<size_t>::max() /
sizeof(int4);
1592 constexpr size_t kMaxAddressableSize = std::numeric_limits<size_t>::max();
1593 size_t key_count = s0s.size();
1594 if (key_count == 0)
return cudaSuccess;
1597 if (key_count > kMaxElements ||
1598 key_count > kMaxAddressableSize / kOutputsPerKey /
sizeof(int4) ||
1599 (stride != 0 && (stride < key_count || stride > kMaxElements)))
1600 return cudaErrorInvalidValue;
1602 const size_t resolved_stride = stride == 0 ? key_count : stride;
1603 if (resolved_stride > (kMaxElements - key_count) /
static_cast<size_t>(in_bits))
1604 return cudaErrorInvalidValue;
1605 const size_t required =
static_cast<size_t>(in_bits) * resolved_stride + key_count;
1606 if (cws.size() < required || tr.size() < key_count ||
1607 ys.size() < key_count * kOutputsPerKey ||
1608 s0s.data() ==
nullptr || cws.data() ==
nullptr || tr.data() ==
nullptr ||
1609 ys.data() ==
nullptr)
return cudaErrorInvalidValue;
1610 if (key_count >
static_cast<size_t>(std::numeric_limits<int>::max()) / LaunchShape::kBlocksPerKey)
1611 return cudaErrorInvalidConfiguration;
1612 gpu::detail::DpfSplitEvalAllBatchKernel<Scheme, LaunchShape::kFrontierDepth, LaunchShape::kBlockRootDepth>
1613 <<<key_count * LaunchShape::kBlocksPerKey, LaunchShape::kThreadsPerBlock, 0, stream>>>(
1614 b, s0s.data(), cws.data(), tr.data(), resolved_stride, ys.data(), prg_);
1615 return cudaGetLastError();
1617 static_assert(
sizeof(Prg) == 0,
"gpu launch requires a CUDA compiler");
1618 return cudaErrorNotSupported;
1622#if FSS_ENABLE_V1_COMPAT
1628template <
int in_bits,
typename Group,
typename Prg,
typename In>
1632 Cw cws[],
const int4 s0s[2], In a, int4 b_buf) {
1633 int4 seed[in_bits + 1];
1635 Gen(cuda::std::span<int4>(seed, in_bits + 1), tr,
1636 cuda::std::span<const int4, 2>(s0s, 2), a, b_buf);
1637 for (
int i = 0; i < in_bits; ++i) cws[i] = {seed[i], ((tr >> i) & 1) != 0};
1639 cws[in_bits] = {seed[in_bits],
false};
1642template <
int in_bits,
typename Group,
typename Prg,
typename In>
1644 Groupable<Group> && Prgable<Prg, 2>)
1646 bool b, int4 s0,
const Cw cws[], In x) {
1647 int4 seed[in_bits + 1];
1649 for (
int i = 0; i <= in_bits; ++i) seed[i] = cws[i].s;
1650 for (
int i = 0; i < in_bits; ++i) tr |= static_cast<In>(cws[i].tr) << i;
1651 return Eval(b, s0, cuda::std::span<const int4>(seed, in_bits + 1), tr, x);
1654template <
int in_bits,
typename Group,
typename Prg,
typename In>
1656 Groupable<Group> && Prgable<Prg, 2>)
1658 bool b, int4 s0,
const Cw cws[], int4 ys[]) {
1659 int4 seed[in_bits + 1];
1661 for (
int i = 0; i <= in_bits; ++i) seed[i] = cws[i].s;
1662 for (
int i = 0; i < in_bits; ++i) tr |= static_cast<In>(cws[i].tr) << i;
1663 EvalAll(b, s0, cuda::std::span<const int4>(seed, in_bits + 1), tr, ys);
1670template <
int in_bits,
typename Group,
typename Prg,
typename In>
1672 Groupable<Group> && Prgable<Prg, 2>)
1674 Key &k0, Key &k1, cuda::std::span<const int4, 2> s0s, In a, int4 b_buf) {
1675 int4 seed[in_bits + 1];
1677 Gen(cuda::std::span<int4>(seed, in_bits + 1), tr, s0s, a, b_buf);
1678 for (
int i = 0; i < in_bits; ++i) {
1679 k0.cws[i] = {seed[i], ((tr >> i) & 1) != 0};
1680 k1.cws[i] = k0.cws[i];
1683 k0.cws[in_bits] = {seed[in_bits],
false};
1684 k1.cws[in_bits] = k0.cws[in_bits];
1689template <
int in_bits,
typename Group,
typename Prg,
typename In>
1691 Groupable<Group> && Prgable<Prg, 2>)
1693 bool b,
const Key &key, In x) {
1694 int4 seed[in_bits + 1];
1696 for (
int i = 0; i <= in_bits; ++i) seed[i] = key.cws[i].s;
1697 for (
int i = 0; i < in_bits; ++i) tr |= static_cast<In>(key.cws[i].tr) << i;
1698 return Eval(b, key.s0, cuda::std::span<const int4>(seed, in_bits + 1), tr, x);
1701template <
int in_bits,
typename Group,
typename Prg,
typename In>
1703 Groupable<Group> && Prgable<Prg, 2>)
1705 bool b,
const Key &key, cuda::std::span<int4> ys) {
1706 int4 seed[in_bits + 1];
1708 for (
int i = 0; i <= in_bits; ++i) seed[i] = key.cws[i].s;
1709 for (
int i = 0; i < in_bits; ++i) tr |= static_cast<In>(key.cws[i].tr) << i;
1710 EvalAll(b, key.s0, cuda::std::span<const int4>(seed, in_bits + 1), tr, ys.data());
2-party DPF scheme.
Definition dpf.cuh:112
static constexpr int kCwCount
Definition dpf.cuh:150
cudaError_t EvalAllGpu(bool b, int4 s0, cuda::std::span< const int4 > cws, In tr, cuda::std::span< int4 > ys, cudaStream_t stream=0, size_t stride=1) const
Launch full-domain CUDA evaluation from split device arrays.
Definition dpf.cuh:1552
Dpf(Prg prg)
Configure the pseudorandom generator.
Definition dpf.cuh:1013
int4 Eval(bool b, int4 s0, cuda::std::span< const int4 > cws, In tr, In x, size_t stride=1)
Evaluate one point using separate correction components.
Definition dpf.cuh:1102
cudaError_t EvalRangeGpu(bool b, In first, In last, int4 s0, cuda::std::span< const int4 > cws, In tr, cuda::std::span< int4 > ys, cudaStream_t stream=0, size_t stride=1) const
Evaluate the closed interval [first, last] on the given CUDA stream from split arrays.
Definition dpf.cuh:1519
void EvalAll(bool b, int4 s0, cuda::std::span< const int4 > cws, In tr, int4 ys[], int par_depth=0, size_t stride=1)
Evaluate the full domain on the host from split correction words.
Definition dpf.cuh:1221
__uint128_t Input
Definition dpf.cuh:152
void Gen(cuda::std::span< int4 > cws, In &tr, cuda::std::span< const int4, 2 > s0s, In a, int4 b_buf, size_t stride=1)
Generate correction words in a caller-strided layout and packed control bits.
Definition dpf.cuh:1019
void EvalRange(bool b, In first, In last, int4 s0, cuda::std::span< const int4 > cws, In tr, cuda::std::span< int4 > ys, int par_depth=0, size_t stride=1)
Evaluate the closed input interval [first, last] on the host from split correction words.
Definition dpf.cuh:1130
cudaError_t EvalAllGpuBatch(bool b, cuda::std::span< const int4 > s0s, cuda::std::span< const int4 > cws, cuda::std::span< const __uint128_t > tr, cuda::std::span< int4 > ys, cudaStream_t stream=0, size_t stride=0) const
2-party DCF scheme over F2 from standard DPF (Grotto construction).
Definition grotto_dcf.cuh:103
Single build-time switch for the deprecated v1 Cw-form compatibility entries.
Group interface.
Definition group.cuh:49
Pseudorandom generator (PRG) interface.
Definition prg.cuh:24
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
Correction word.
Definition dpf.cuh:123
int4 s
Seed correction, with the left control correction in its least significant bit.
Definition dpf.cuh:124
bool tr
Right control correction.
Definition dpf.cuh:125
One party's key in the aggregate Cw layout: the correction words plus the party's initial seed.
Definition dpf.cuh:137
int4 s0
This party's initial seed.
Definition dpf.cuh:139
Cw cws[in_bits+1]
Tree correction words followed by the output correction word.
Definition dpf.cuh:138