38#include <cuda_runtime.h>
39#include <cuda/std/array>
40#include <cuda/std/span>
41#include <cuda/std/tuple>
72template <
typename Cw,
typename In,
int in_bits>
78 __host__ __device__
inline Cw operator[](
int i)
const {
79 const int4 s_cw = s[
static_cast<size_t>(i) * stride];
80 if (i >= in_bits)
return Cw{s_cw,
false};
81 return Cw{s_cw, ((packed_tr >> i) & 1) != 0};
112template <
int in_bits,
typename Group,
typename Prg,
typename XorHash,
typename Hash,
typename In = u
int>
114 Groupable<Group> && Prgable<Prg, 2> && XorHashable<XorHash> && Hashable<Hash>)
129 struct __align__(32)
Cw {
134 static_assert(
sizeof(
Cw) == 32);
148 __host__ __device__
explicit Vdpf(Prg prg, XorHash xor_hash, Hash hash);
192 __host__ __device__
int Gen(cuda::std::span<int4> cws, In &tr, cuda::std::span<int4, 4> cs,
193 cuda::std::span<const int4, 2> s0s, In a, int4 b_buf,
size_t stride = 1);
225 __host__ __device__ cuda::std::array<int4, 4>
Eval(
bool b, int4 s0, cuda::std::span<const int4> cws,
226 In tr, cuda::std::span<const int4, 4> cs, In x, int4 &y,
size_t stride = 1);
240 void Prove(cuda::std::span<
const cuda::std::array<int4, 4>> pi_tildes, cuda::std::span<const int4, 4> cs,
241 cuda::std::span<int4, 4> pi);
250 __host__ __device__
static bool Verify(cuda::std::span<const int4, 4> pi0, cuda::std::span<const int4, 4> pi1);
278 void EvalAll(
bool b, int4 s0, cuda::std::span<const int4> cws, In tr,
279 cuda::std::span<const int4, 4> cs, cuda::std::span<int4> ys, cuda::std::span<int4, 4> pi,
280 int par_depth = 0,
size_t stride = 1);
317 void EvalRange(
bool b, In first, In last, int4 s0, cuda::std::span<const int4> cws,
318 In tr, cuda::std::span<const int4, 4> cs, cuda::std::span<int4> ys,
319 cuda::std::span<int4, 4> pi,
int par_depth = 0,
size_t stride = 1);
340 void ProveMerkle(cuda::std::span<
const cuda::std::array<int4, 4>> pi_tildes, cuda::std::span<const int4, 4> cs,
341 cuda::std::span<int4, 4> pi,
int par_depth = 0);
388 void BatchEval(
bool b, int4 s0, cuda::std::span<const int4> cws, In tr,
389 cuda::std::span<const int4, 4> cs, cuda::std::span<const In> xs,
390 cuda::std::span<int4> ys, cuda::std::span<int4, 4> pi,
size_t stride = 1);
439 cuda::std::span<const int4, 4> cs, cuda::std::span<const In> xs,
440 cuda::std::span<int4> ys, cuda::std::span<int4, 4> pi,
size_t stride = 1);
474 cuda::std::span<const int4, 4> cs, cuda::std::span<int4> ys, cuda::std::span<int4, 4> pi,
475 int par_depth = 0,
size_t stride = 1);
515 template <
int frontier_depth = 16,
int threads_per_block = 256>
517 cuda::std::span<const int4> cws, In tr, cuda::std::span<const int4, 4> cs,
518 cuda::std::span<int4> ys, cuda::std::span<int4, 4> pi,
519 cudaStream_t stream = 0,
size_t stride = 1) const
520 requires(in_bits < sizeof(
size_t) * 8);
558 cuda::std::span<const int4> cws, In tr, cuda::std::span<const int4, 4> cs,
559 cuda::std::span<int4> ys, cuda::std::span<int4, 4> pi,
int par_depth = 0,
606 template <
int frontier_depth = 16,
int threads_per_block = 256>
608 cuda::std::span<const int4> cws, In tr, cuda::std::span<const int4, 4> cs,
609 cuda::std::span<int4> ys, cuda::std::span<int4, 4> pi,
610 cudaStream_t stream = 0,
size_t stride = 1) const;
669 template <
int threads_per_block = 256>
671 cuda::std::span<const int4> cws, In tr, cuda::std::span<const int4, 4> cs,
672 cuda::std::span<const In> xs,
673 cuda::std::span<int4> ys, cuda::std::span<int4, 4> pi,
674 cudaStream_t stream = 0,
size_t stride = 1) const;
676#if FSS_ENABLE_V1_COMPAT
689 [[deprecated(
"use the split-component Gen(cuda::std::span<int4>, In&, cuda::std::span<int4, 4>, cuda::std::span<const int4, 2>, In, int4, size_t) overload")]]
690 __host__ __device__
int Gen(Cw cws[], cuda::std::array<int4, 4> &cs, int4 &ocw,
691 cuda::std::span<const int4, 2> s0s, In a, int4 b_buf);
694 [[deprecated(
"use the split-component Eval(bool, int4, cuda::std::span<const int4>, In, cuda::std::span<const int4, 4>, In, int4&, size_t) overload")]]
695 __host__ __device__ cuda::std::array<int4, 4>
Eval(
bool b, int4 s0,
696 cuda::std::span<const Cw> cws, cuda::std::span<const int4, 4> cs, int4 ocw,
700 [[deprecated(
"use the split-component EvalAll(bool, int4, cuda::std::span<const int4>, In, cuda::std::span<const int4, 4>, cuda::std::span<int4>, cuda::std::span<int4, 4>, int, size_t) overload")]]
701 void EvalAll(
bool b, int4 s0, cuda::std::span<const Cw> cws,
702 cuda::std::span<const int4, 4> cs, int4 ocw, cuda::std::span<int4> ys,
703 cuda::std::array<int4, 4> &pi);
715 using Key [[deprecated(
"use the split-component Gen/Eval/EvalAll with separate cws, tr, and cs arrays")]] =
716 compat::detail::VdpfKeyRecord<Vdpf, Cw, in_bits>;
719 using EvalResult [[deprecated(
"use the split-component Eval(bool, int4, cuda::std::span<const int4>, In, cuda::std::span<const int4, 4>, In, int4&, size_t) overload")]] =
720 compat::detail::EvalResultRecord<Vdpf>;
723 [[deprecated(
"use the split-component Eval(bool, int4, cuda::std::span<const int4>, In, cuda::std::span<const int4, 4>, In, int4&, size_t) overload")]]
724 __host__ __device__ compat::detail::EvalResultRecord<Vdpf>
Eval(
725 bool b,
const compat::detail::VdpfKeyRecord<Vdpf, Cw, in_bits> &key, In x);
728 [[deprecated(
"use the split-component EvalAll(bool, int4, cuda::std::span<const int4>, In, cuda::std::span<const int4, 4>, cuda::std::span<int4>, cuda::std::span<int4, 4>, int, size_t) overload")]]
729 void EvalAll(
bool b,
const compat::detail::VdpfKeyRecord<Vdpf, Cw, in_bits> &key,
730 cuda::std::span<int4> ys, cuda::std::array<int4, 4> &pi);
733 [[deprecated(
"use the split-component EvalAllMerkle(bool, int4, cuda::std::span<const int4>, In, cuda::std::span<const int4, 4>, cuda::std::span<int4>, cuda::std::span<int4, 4>, int, size_t) overload")]]
734 void EvalAllMerkle(
bool b,
const compat::detail::VdpfKeyRecord<Vdpf, Cw, in_bits> &key,
735 cuda::std::span<int4> ys, cuda::std::array<int4, 4> &pi);
739 friend class detail::SchemeAccess;
745 template <
int frontier_depth,
int threads_per_block>
746 struct GpuLaunchShape;
749 using CwsView = detail::VdpfCwsView<Cw, In, in_bits>;
755 __host__ __device__
void Write(
int i, int4 s_cw,
bool tr_cw)
const {
760 int4 *cw_slot =
reinterpret_cast<int4 *
>(&cws[i]);
762 cw_slot[1] = make_int4(tr_cw, 0, 0, 0);
765 __host__ __device__
void Finish(int4 terminal)
const {
766 cws[in_bits].s = terminal;
767 cws[in_bits].tr =
false;
778 __host__ __device__
void Write(
int i, int4 s_cw,
bool tr_cw) {
779 cws[
static_cast<size_t>(i) * stride] = s_cw;
780 packed_tr |=
static_cast<In
>(tr_cw) << i;
783 __host__ __device__
void Finish(int4 terminal) {
784 cws[
static_cast<size_t>(in_bits) * stride] = terminal;
792 template <
typename Writer>
793 __host__ __device__
int Generate(Writer writer, cuda::std::span<int4, 4> cs,
const int4 *s0s, In a, int4 b_buf);
796 template <
typename Cws>
797 __host__ __device__ cuda::std::array<int4, 4> EvalCws(
798 bool b, int4 s0, Cws cws, cuda::std::span<const int4, 4> cs, In x, int4 &y);
802 template <
typename Cws>
803 void EvalRangeCws(
bool b, In first, In last, int4 s0, Cws cws,
804 cuda::std::span<const int4, 4> cs, cuda::std::span<int4> ys,
805 cuda::std::span<int4, 4> pi,
int par_depth);
809 template <
typename Cws>
810 void EvalRangeMerkleCws(
bool b, In first, In last, int4 s0, Cws cws,
811 cuda::std::span<const int4, 4> cs, cuda::std::span<int4> ys,
812 cuda::std::span<int4, 4> pi,
int par_depth);
815 template <
bool merkle,
typename Cws>
816 void EvalBatchCws(
bool b, int4 s0, Cws cws, cuda::std::span<const int4, 4> cs,
817 cuda::std::span<const In> xs, cuda::std::span<int4> ys, cuda::std::span<int4, 4> pi);
819 template <
typename Cws>
821 int4 st, Cws cws, cuda::std::span<int4> ys,
size_t l,
size_t r,
int i,
int par_depth_);
823 template <
typename Cws>
824 void EvalPrunedTree(int4 st, Cws cws, cuda::std::span<int4> ys, In l, In r,
int i,
825 int par_depth_, In first, In last);
832template <
typename Hash>
833__host__ __device__
inline cuda::std::array<int4, 2> VdpfMerkleFoldNode(
834 Hash &hash,
const cuda::std::array<int4, 2> *nodes,
size_t count,
size_t index) {
835 const size_t left = 2 * index;
836 if (left + 1 == count)
return nodes[left];
837 const int4 msg[4] = {nodes[left][0], nodes[left][1], nodes[left + 1][0], nodes[left + 1][1]};
838 return hash.Hash(cuda::std::span<const int4, 4>(msg));
843namespace gpu::detail {
845#if defined(__CUDACC__)
850__device__
inline int4 VdpfCwSeed(
const int4 *cws,
size_t stride,
int level) {
851 return cws[
static_cast<size_t>(level) * stride];
854template <
typename In>
855__device__
inline bool VdpfCwRight(
const In &packed_tr,
int level) {
856 return ((packed_tr >> level) & 1) != 0;
859template <
int in_bits,
int threads_per_block,
typename Group,
typename Prg,
typename XorHash,
860 typename Hash,
typename In>
861__device__
void VdpfRangeMerkleLeavesWork(
bool b, In first,
862 int subtree_depth,
int frontier_depth,
int block_root_depth, int4 s0,
const int4 *cws,
863 In packed_tr,
size_t stride,
const int4 *cs,
864 int4 *ys, cuda::std::array<int4, 2> *leaves, Prg prg,
865 XorHash xor_hash, Hash hash) {
866 using MathScheme = Dpf<in_bits, Group, Prg, In>;
867 const int root_depth = in_bits - subtree_depth;
868 const int block_subtree_depth = frontier_depth - block_root_depth;
869 const int tail_depth = subtree_depth - frontier_depth;
870 __shared__ int4 frontier[threads_per_block];
871 if (threadIdx.x == 0) {
873 for (
int level = 0; level < root_depth; ++level) {
874 const bool right = (first >> (in_bits - level - 1)) & 1;
875 st = fss::detail::SchemeAccess::DpfPathStep<MathScheme>(
876 prg, st, VdpfCwSeed(cws, stride, level), VdpfCwRight(packed_tr, level), right);
878 for (
int level = 0; level < block_root_depth; ++level) {
879 const bool right = (blockIdx.x >> (block_root_depth - level - 1)) & 1;
880 st = fss::detail::SchemeAccess::DpfPathStep<MathScheme>(prg, st,
881 VdpfCwSeed(cws, stride, root_depth + level),
882 VdpfCwRight(packed_tr, root_depth + level), right);
887 for (
int level = 0; level < block_subtree_depth; ++level) {
888 const bool active = threadIdx.x < (1u << (level + 1));
889 int4 st = {0, 0, 0, 0};
890 if (active) st = frontier[threadIdx.x >> 1];
894 const int cw_index = root_depth + block_root_depth + level;
895 fss::detail::SchemeAccess::DpfExpandNode<MathScheme>(prg, st,
896 VdpfCwSeed(cws, stride, cw_index), VdpfCwRight(packed_tr, cw_index), left, right);
897 frontier[threadIdx.x] = (threadIdx.x & 1) ? right : left;
902 int4 node = frontier[threadIdx.x];
903 const int frontier_level = root_depth + frontier_depth;
904 int level = frontier_level;
905 const int count = 1 << tail_depth;
906 const size_t thread_index =
static_cast<size_t>(blockIdx.x) * blockDim.x + threadIdx.x;
907 const size_t offset = thread_index << tail_depth;
908 const auto cw = Group::From(cws[
static_cast<size_t>(in_bits) * stride]);
909 for (
int i = 0; i < count; ++i) {
910 while (level < in_bits) {
912 fss::detail::SchemeAccess::DpfExpandNode<MathScheme>(prg, node,
913 VdpfCwSeed(cws, stride, level), VdpfCwRight(packed_tr, level), left, right);
914 pending[level - frontier_level] = right;
920 auto value = Group::From(seed);
921 if (t) value = value + cw;
922 if (b) value = -value;
923 ys[offset + i] = value.Into();
924 const In x =
static_cast<In
>(first + offset + i);
925 auto corrected = xor_hash.Hash(cuda::std::tuple<int4, const int4>{fss::detail::Pack(x), seed});
926 if (t) corrected =
util::Xor(cuda::std::span<const int4, 4>(corrected),
927 cuda::std::span<const int4, 4>(cs, 4));
928 leaves[offset + i] = hash.Hash(cuda::std::span<const int4, 4>(corrected));
932 while ((next & 1) == 0) {
936 node = pending[level - frontier_level];
941template <
int in_bits,
int threads_per_block,
typename Group,
typename Prg,
typename XorHash,
942 typename Hash,
typename In>
943__global__
void VdpfRangeMerkleLeavesKernel(
bool b, In first,
int subtree_depth,
944 int frontier_depth,
int block_root_depth, int4 s0,
const int4 *cws,
945 In packed_tr,
size_t stride,
const int4 *cs,
946 int4 *ys, cuda::std::array<int4, 2> *leaves, Prg prg,
947 XorHash xor_hash, Hash hash) {
948 VdpfRangeMerkleLeavesWork<in_bits, threads_per_block, Group, Prg, XorHash, Hash, In>(b, first, subtree_depth,
949 frontier_depth, block_root_depth, s0, cws, packed_tr, stride, cs, ys, leaves, prg,
955template <
int in_bits,
int threads_per_block,
typename Group,
typename Prg,
typename XorHash,
956 typename Hash,
typename In>
957__global__
void VdpfRangeMerkleLeavesDeviceKeyKernel(
bool b, In first,
958 int subtree_depth,
int frontier_depth,
int block_root_depth,
const int4 *s0,
959 const int4 *cws,
const In *tr,
size_t stride,
const int4 *cs,
960 int4 *ys, cuda::std::array<int4, 2> *leaves, Prg prg,
961 XorHash xor_hash, Hash hash) {
962 VdpfRangeMerkleLeavesWork<in_bits, threads_per_block, Group, Prg, XorHash, Hash, In>(b, first, subtree_depth,
963 frontier_depth, block_root_depth, s0[0], cws, tr[0], stride, cs, ys, leaves, prg,
968template <
typename Hash>
969__global__
void VdpfRangeMerkleFoldKernel(
const cuda::std::array<int4, 2> *nodes,
970 size_t count, cuda::std::array<int4, 2> *next, Hash hash) {
971 const size_t index =
static_cast<size_t>(blockIdx.x) * blockDim.x + threadIdx.x;
972 if (index < count / 2 + count % 2)
973 next[index] = fss::detail::VdpfMerkleFoldNode(hash, nodes, count, index);
976template <
typename Node>
977__global__
void VdpfRangeMerkleProofKernel(
const Node *root,
978 const int4 *cs, int4 *pi) {
989template <
int in_bits,
typename Group,
typename Prg,
typename XorHash,
typename Hash,
typename In>
990__device__
void VdpfBatchMerkleLeavesWork(
bool b, int4 s0,
const int4 *cws,
991 In packed_tr,
size_t stride,
const int4 *cs,
992 const In *xs, int4 *ys, cuda::std::array<int4, 2> *leaves,
size_t count,
993 Prg prg, XorHash xor_hash,
995 using MathScheme = Dpf<in_bits, Group, Prg, In>;
996 const size_t index =
static_cast<size_t>(blockIdx.x) * blockDim.x + threadIdx.x;
997 if (index >= count)
return;
998 const In x = xs[index];
1000 for (
int level = 0; level < in_bits; ++level) {
1001 const bool right = (x >> (in_bits - level - 1)) & 1;
1002 node = fss::detail::SchemeAccess::DpfPathStep<MathScheme>(
1003 prg, node, VdpfCwSeed(cws, stride, level), VdpfCwRight(packed_tr, level), right);
1007 auto value = Group::From(seed);
1008 const auto cw = Group::From(cws[
static_cast<size_t>(in_bits) * stride]);
1009 if (t) value = value + cw;
1010 if (b) value = -value;
1011 ys[index] = value.Into();
1012 auto corrected = xor_hash.Hash(cuda::std::tuple<int4, const int4>{fss::detail::Pack(x), seed});
1013 if (t) corrected =
util::Xor(cuda::std::span<const int4, 4>(corrected),
1014 cuda::std::span<const int4, 4>(cs, 4));
1015 leaves[index] = hash.Hash(cuda::std::span<const int4, 4>(corrected));
1017template <
int in_bits,
typename Group,
typename Prg,
typename XorHash,
typename Hash,
typename In>
1018__global__
void VdpfBatchMerkleLeavesKernel(
bool b, int4 s0,
const int4 *cws,
1019 In packed_tr,
size_t stride,
const int4 *cs,
1020 const In *xs, int4 *ys, cuda::std::array<int4, 2> *leaves,
size_t count,
1021 Prg prg, XorHash xor_hash,
1023 VdpfBatchMerkleLeavesWork<in_bits, Group, Prg, XorHash, Hash, In>(b, s0, cws, packed_tr, stride, cs, xs, ys,
1024 leaves, count, prg, xor_hash, hash);
1028template <
int in_bits,
typename Group,
typename Prg,
typename XorHash,
typename Hash,
typename In>
1029__global__
void VdpfBatchMerkleLeavesDeviceKeyKernel(
bool b,
const int4 *s0,
const int4 *cws,
1030 const In *tr,
size_t stride,
const int4 *cs,
1031 const In *xs, int4 *ys, cuda::std::array<int4, 2> *leaves,
1032 size_t count, Prg prg, XorHash xor_hash,
1034 VdpfBatchMerkleLeavesWork<in_bits, Group, Prg, XorHash, Hash, In>(b, s0[0], cws, tr[0], stride, cs, xs, ys,
1035 leaves, count, prg, xor_hash, hash);
1039template <
int in_bits,
int frontier_depth,
int block_root_depth,
typename Group,
typename Prg,
1040 typename XorHash,
typename Hash,
typename In>
1041__device__
void VdpfMerkleFrontierWork(
bool b, int4 s0,
const int4 *cws,
1042 In packed_tr,
size_t stride,
const int4 *cs,
1043 int4 *ys, cuda::std::array<int4, 2> *roots, Prg prg,
1044 XorHash xor_hash, Hash hash) {
1045 using MathScheme = Dpf<in_bits, Group, Prg, In>;
1046 constexpr int kTailDepth = in_bits - frontier_depth;
1047 constexpr int kBlockSubtreeDepth = frontier_depth - block_root_depth;
1048 const int thread_index = blockIdx.x * blockDim.x + threadIdx.x;
1049 if (thread_index >= (1 << frontier_depth))
return;
1052 __shared__ int4 shared_frontier[1 << kBlockSubtreeDepth];
1053 if (threadIdx.x == 0) {
1056 for (
int i = 0; i < block_root_depth; ++i) {
1057 bool x_bit = (blockIdx.x >> (block_root_depth - 1 - i)) & 1;
1059 auto node_prg = prg;
1060 st = fss::detail::SchemeAccess::DpfPathStep<MathScheme>(
1061 node_prg, st, VdpfCwSeed(cws, stride, i), VdpfCwRight(packed_tr, i), x_bit);
1064 shared_frontier[0] = st;
1070 for (
int i = 0; i < kBlockSubtreeDepth; ++i) {
1071 const bool active = threadIdx.x < (1 << (i + 1));
1072 int4 n = {0, 0, 0, 0};
1073 if (active) n = shared_frontier[threadIdx.x >> 1];
1079 auto node_prg = prg;
1080 fss::detail::SchemeAccess::DpfExpandNode<MathScheme>(node_prg, n,
1081 VdpfCwSeed(cws, stride, block_root_depth + i),
1082 VdpfCwRight(packed_tr, block_root_depth + i), left, right);
1084 shared_frontier[threadIdx.x] = (threadIdx.x & 1) ? right : left;
1092 int4 nodes[1 << kTailDepth];
1093 nodes[0] = shared_frontier[threadIdx.x];
1096 for (
int lev = frontier_depth; lev < in_bits; ++lev) {
1098 for (
int k = count - 1; k >= 0; --k) {
1101 auto node_prg = prg;
1102 fss::detail::SchemeAccess::DpfExpandNode<MathScheme>(node_prg, nodes[k],
1103 VdpfCwSeed(cws, stride, lev), VdpfCwRight(packed_tr, lev), left, right);
1105 nodes[2 * k] = left;
1106 nodes[2 * k + 1] = right;
1111 auto ocw_group = Group::From(cws[
static_cast<size_t>(in_bits) * stride]);
1112 const int output_offset = thread_index << kTailDepth;
1113 cuda::std::array<int4, 2> stack[kTailDepth + 1];
1115 for (
int k = 0; k < count; ++k) {
1116 int4 st_cur = nodes[k];
1121 auto g = Group::From(s_cur);
1122 if (t_cur) g = g + ocw_group;
1124 ys[output_offset + k] = g.Into();
1127 int4 x_buf = fss::detail::Pack(
static_cast<In
>(output_offset + k));
1128 auto pi_tilde = xor_hash.Hash(cuda::std::tuple<int4, const int4>{x_buf, s_cur});
1130 pi_tilde =
util::Xor(cuda::std::span<const int4, 4>(pi_tilde), cuda::std::span<const int4, 4>(cs, 4));
1132 int4 leaf_msg[4] = {pi_tilde[0], pi_tilde[1], pi_tilde[2], pi_tilde[3]};
1133 cuda::std::array<int4, 2> node = hash.Hash(cuda::std::span<const int4, 4>(leaf_msg));
1140 while (rest != 0 && (rest & 1) == 1) {
1141 int4 fold_msg[4] = {stack[lvl][0], stack[lvl][1], node[0], node[1]};
1142 node = hash.Hash(cuda::std::span<const int4, 4>(fold_msg));
1151 __shared__ cuda::std::array<int4, 2> s_node[1 << kBlockSubtreeDepth];
1152 s_node[threadIdx.x] = stack[kTailDepth];
1155 for (
int stride = 1; stride < (1 << kBlockSubtreeDepth); stride <<= 1) {
1156 int idx = 2 * stride * threadIdx.x;
1157 if (idx < (1 << kBlockSubtreeDepth)) {
1158 int4 fold_msg[4] = {s_node[idx][0], s_node[idx][1], s_node[idx + stride][0], s_node[idx + stride][1]};
1159 s_node[idx] = hash.Hash(cuda::std::span<const int4, 4>(fold_msg));
1163 if (threadIdx.x == 0) roots[blockIdx.x] = s_node[0];
1165template <
int in_bits,
int frontier_depth,
int block_root_depth,
typename Group,
typename Prg,
1166 typename XorHash,
typename Hash,
typename In>
1167__global__
void VdpfMerkleFrontierKernel(
bool b, int4 s0,
const int4 *cws,
1168 In packed_tr,
size_t stride,
const int4 *cs,
1169 int4 *ys, cuda::std::array<int4, 2> *roots, Prg prg,
1170 XorHash xor_hash, Hash hash) {
1171 VdpfMerkleFrontierWork<in_bits, frontier_depth, block_root_depth, Group, Prg, XorHash, Hash, In>(
1172 b, s0, cws, packed_tr, stride, cs, ys, roots, prg, xor_hash, hash);
1176template <
int in_bits,
int frontier_depth,
int block_root_depth,
typename Group,
typename Prg,
1177 typename XorHash,
typename Hash,
typename In>
1178__global__
void VdpfMerkleFrontierDeviceKeyKernel(
bool b,
const int4 *s0,
const int4 *cws,
1179 const In *tr,
size_t stride,
const int4 *cs,
1180 int4 *ys, cuda::std::array<int4, 2> *roots, Prg prg,
1181 XorHash xor_hash, Hash hash) {
1182 VdpfMerkleFrontierWork<in_bits, frontier_depth, block_root_depth, Group, Prg, XorHash, Hash, In>(
1183 b, s0[0], cws, tr[0], stride, cs, ys, roots, prg, xor_hash, hash);
1190template <
int threads_per_block,
typename Hash>
1191__global__
void VdpfMerkleFinalizeKernel(
const cuda::std::array<int4, 2> *roots,
int nroots,
const int4 *cs,
1192 int4 *pi, Hash hash) {
1193 __shared__ cuda::std::array<int4, 2> s_node[threads_per_block];
1194 if (threadIdx.x < nroots) s_node[threadIdx.x] = roots[threadIdx.x];
1196 for (
int stride = 1; stride < nroots; stride <<= 1) {
1197 int idx = 2 * stride * threadIdx.x;
1199 int4 fold_msg[4] = {s_node[idx][0], s_node[idx][1], s_node[idx + stride][0], s_node[idx + stride][1]};
1200 s_node[idx] = hash.Hash(cuda::std::span<const int4, 4>(fold_msg));
1204 if (threadIdx.x == 0) {
1205 pi[0] = s_node[0][0];
1206 pi[1] = s_node[0][1];
1219template <
int in_bits,
int unroll,
bool b,
typename Group,
typename Prg,
typename XorHash,
1220 typename In,
typename Query>
1221__global__
void VdpfEvalPointKernel(int4 *ys, cuda::std::array<int4, 4> *pi,
const int4 *seeds,
1222 const int4 *cws,
const In *tr,
const cuda::std::array<int4, 4> *cs,
const Query *xs,
1223 int nkeys, Prg prg, XorHash xor_hash) {
1224 static_assert(in_bits <=
sizeof(In) * 8,
"In too narrow for in_bits");
1225 int tid = blockIdx.x * blockDim.x + threadIdx.x;
1226 if (tid >= nkeys)
return;
1229 In x =
static_cast<In
>(xs[tid]);
1230 const In packed_tr = tr[tid];
1232#pragma unroll unroll
1233 for (
int i = 0; i < in_bits; ++i) {
1234 int4 s_cw = cws[
static_cast<size_t>(i) * nkeys + tid];
1237 bool tr_cw = ((packed_tr >> i) & 1) != 0;
1239 auto g = prg.Gen(s);
1240 int4 sl = g[0], sr = g[1];
1253 bool x_bit = GetBit(x, in_bits - 1 - i);
1263 auto y = Group::From(s);
1264 if (t) y = y + Group::From(cws[
static_cast<size_t>(in_bits) * nkeys + tid]);
1265 if constexpr (b) y = -y;
1268 int4 x_buf = fss::detail::Pack(x);
1269 auto pi_tilde = xor_hash.Hash(cuda::std::tuple<int4, const int4>{x_buf, s});
1271 pi_tilde =
util::Xor(cuda::std::span<const int4, 4>(pi_tilde), cuda::std::span<const int4, 4>(cs[tid]));
1274 for (
int j = 0; j < 4; ++j) pi[tid][j] = pi_tilde[j];
1298template <
int unroll = 4,
int in_bits,
typename Group,
typename Prg,
typename XorHash,
typename Hash,
typename In,
1299 typename Query = In>
1300 requires QueryFor<Query, in_bits>
1301void VdpfEvalPointGpu(
bool b, cuda::std::span<const int4> seeds, cuda::std::span<const int4> cws,
1302 cuda::std::span<const In> tr, cuda::std::span<
const cuda::std::array<int4, 4>> cs,
1303 cuda::std::span<const Query> xs, int4 *ys, cuda::std::span<cuda::std::array<int4, 4>> pi,
int nkeys,
1304 const Vdpf<in_bits, Group, Prg, XorHash, Hash, In> &vdpf, cudaStream_t stream = 0) {
1306 throw std::invalid_argument(
"VdpfEvalPointGpu: nkeys must be nonnegative");
1307 if (nkeys == 0)
return;
1308 const size_t count =
static_cast<size_t>(nkeys);
1309 constexpr size_t kLevels =
static_cast<size_t>(in_bits) + 1;
1310 if (count > SIZE_MAX / kLevels || cws.size() < count * kLevels)
1311 throw std::invalid_argument(
"VdpfEvalPointGpu: cws must hold (in_bits+1)*nkeys elements");
1312 if (seeds.size() < count || tr.size() < count || cs.size() < count || xs.size() < count ||
1313 ys ==
nullptr || pi.size() < count)
1314 throw std::invalid_argument(
"VdpfEvalPointGpu: seeds, tr, cs, xs, ys, and pi must cover nkeys entries");
1315 constexpr int kBs = 256;
1316 int blocks = (nkeys + kBs - 1) / kBs;
1318 VdpfEvalPointKernel<in_bits, unroll, true, Group, Prg, XorHash, In, Query><<<blocks, kBs, 0, stream>>>(
1319 ys, pi.data(), seeds.data(), cws.data(), tr.data(), cs.data(), xs.data(), nkeys,
1320 fss::detail::SchemeAccess::GetPrg(vdpf), fss::detail::SchemeAccess::GetXorHash(vdpf));
1322 VdpfEvalPointKernel<in_bits, unroll, false, Group, Prg, XorHash, In, Query><<<blocks, kBs, 0, stream>>>(
1323 ys, pi.data(), seeds.data(), cws.data(), tr.data(), cs.data(), xs.data(), nkeys,
1324 fss::detail::SchemeAccess::GetPrg(vdpf), fss::detail::SchemeAccess::GetXorHash(vdpf));
1330template <
int threads_per_block,
typename Hash>
1331__host__
inline cudaError_t VdpfFoldNodesToProof(cuda::std::array<int4, 2> *nodes,
1332 cuda::std::array<int4, 2> *next,
size_t count,
const int4 *cs, int4 *pi,
const Hash &hash,
1333 cudaStream_t stream, DeviceScratch *first, DeviceScratch *second) {
1334 cudaError_t status = cudaSuccess;
1336 const size_t next_count = count / 2 + count % 2;
1337 const size_t blocks = next_count / threads_per_block + (next_count % threads_per_block != 0);
1338 if (blocks >
static_cast<size_t>(INT_MAX))
return cudaErrorInvalidConfiguration;
1339 VdpfRangeMerkleFoldKernel<<<static_cast<int>(blocks), threads_per_block, 0, stream>>>(
1340 nodes, count, next, hash);
1341 status = cudaGetLastError();
1342 if (status != cudaSuccess)
return status;
1343 auto *previous = nodes;
1348 VdpfRangeMerkleProofKernel<<<1, 1, 0, stream>>>(nodes, cs, pi);
1349 return ReleaseScratch(cudaGetLastError(), {first, second});
1355template <
int in_bits,
typename Group,
typename Prg,
typename XorHash,
typename Hash,
1356 typename In,
int frontier_depth = 16,
int threads_per_block = 256>
1357__host__
inline cudaError_t VdpfEvalAllMerkleGpuDeviceKey(
bool b,
const int4 *s0,
1358 const int4 *cws,
size_t cws_count,
const In *tr,
size_t stride,
const int4 *cs,
1359 int4 *ys,
size_t ys_count, int4 *pi,
const Prg &prg,
const XorHash &xor_hash,
1360 const Hash &hash, cudaStream_t stream) {
1361 constexpr size_t kOutputCount =
size_t{1} << in_bits;
1362 constexpr size_t kMaxElements = SIZE_MAX /
sizeof(int4);
1363 constexpr int kFrontierDepth = frontier_depth < in_bits ? frontier_depth : in_bits;
1364 static_assert(frontier_depth >= 0,
"gpu frontier depth must be nonnegative");
1365 static_assert(1 <= threads_per_block && threads_per_block <= 1024 &&
1366 (threads_per_block & (threads_per_block - 1)) == 0,
1367 "gpu block size must be a power of two in [1,1024]");
1368 static_assert(in_bits - kFrontierDepth <= 8,
1369 "gpu frontier leaves more than eight tail levels; specify a larger frontier_depth");
1370 constexpr int kThreadsPerBlockLog2 = [] {
1371 int requested_log2 = 0;
1372 for (
int threads = threads_per_block; threads > 1; threads >>= 1) ++requested_log2;
1373 return requested_log2 < kFrontierDepth ? requested_log2 : kFrontierDepth;
1375 constexpr int kThreadsPerBlock = 1 << kThreadsPerBlockLog2;
1376 constexpr int kBlockRootDepth = kFrontierDepth - kThreadsPerBlockLog2;
1377 static_assert(kBlockRootDepth <= 30,
"gpu block index exceeds the signed-int range");
1378 constexpr size_t kBlockCount =
size_t{1} << kBlockRootDepth;
1379 static_assert(kBlockCount <= kThreadsPerBlock,
1380 "merkle block roots must fit within one final block");
1381 if (stride == 0 || stride > (kMaxElements - 1) /
static_cast<size_t>(in_bits) ||
1382 cws_count <
static_cast<size_t>(in_bits) * stride + 1 || ys_count < kOutputCount ||
1383 !fss::detail::FitsElementCount<int4>(kOutputCount) ||
1384 !fss::detail::FitsElementCount<cuda::std::array<int4, 2>>(kBlockCount) ||
1385 s0 ==
nullptr || cws ==
nullptr || tr ==
nullptr || cs ==
nullptr || ys ==
nullptr ||
1386 pi ==
nullptr)
return cudaErrorInvalidValue;
1387 DeviceScratch roots_storage(stream);
1388 cudaError_t status = roots_storage.Allocate<cuda::std::array<int4, 2>>(kBlockCount);
1389 if (status != cudaSuccess)
return status;
1390 auto *block_roots = roots_storage.Get<cuda::std::array<int4, 2>>();
1391 VdpfMerkleFrontierDeviceKeyKernel<in_bits, kFrontierDepth, kBlockRootDepth, Group, Prg, XorHash, Hash, In>
1392 <<<kBlockCount, kThreadsPerBlock, 0, stream>>>(
1393 b, s0, cws, tr, stride, cs, ys, block_roots, prg, xor_hash, hash);
1394 status = cudaGetLastError();
1395 if (status != cudaSuccess)
return status;
1396 VdpfMerkleFinalizeKernel<kThreadsPerBlock, Hash>
1397 <<<1, kThreadsPerBlock, 0, stream>>>(
1398 block_roots,
static_cast<int>(kBlockCount), cs, pi, hash);
1399 return ReleaseScratch(cudaGetLastError(), {&roots_storage});
1402template <
int in_bits,
typename Group,
typename Prg,
typename XorHash,
typename Hash,
1403 typename In,
int frontier_depth = 16,
int threads_per_block = 256>
1404__host__
inline cudaError_t VdpfEvalRangeMerkleGpuDeviceKey(
bool b, In first, In last,
1405 const int4 *s0,
const int4 *cws,
size_t cws_count,
const In *tr,
size_t stride,
1406 const int4 *cs, int4 *ys,
size_t ys_count, int4 *pi,
const Prg &prg,
1407 const XorHash &xor_hash,
const Hash &hash, cudaStream_t stream) {
1408 static_assert(frontier_depth >= 0,
"gpu frontier depth must be nonnegative");
1409 static_assert(1 <= threads_per_block && threads_per_block <= 1024 &&
1410 (threads_per_block & (threads_per_block - 1)) == 0,
1411 "gpu block size must be a power of two in [1,1024]");
1412 using Node = cuda::std::array<int4, 2>;
1413 constexpr size_t kMaxElements = SIZE_MAX /
sizeof(int4);
1415 if (!fss::detail::RangeOutputCount<in_bits>(first, last, ys_count, count) || stride == 0 ||
1416 stride > (kMaxElements - 1) /
static_cast<size_t>(in_bits) ||
1417 cws_count <
static_cast<size_t>(in_bits) * stride + 1 ||
1418 s0 ==
nullptr || cws ==
nullptr || tr ==
nullptr || cs ==
nullptr ||
1419 ys ==
nullptr || pi ==
nullptr)
return cudaErrorInvalidValue;
1420 const size_t second_count = count / 2 + count % 2;
1421 constexpr size_t kMaxNodes = size_t(-1) /
sizeof(Node);
1422 if (count > kMaxNodes || second_count > kMaxNodes - count)
return cudaErrorInvalidValue;
1423 constexpr int kThreadBits = [] {
1425 for (
int threads = threads_per_block; threads > 1; threads >>= 1) ++bits;
1428 constexpr int kTailCap = frontier_depth < 30 + kThreadBits ? frontier_depth : 30 + kThreadBits;
1429 constexpr int kChunkDepth = kTailCap + 8 < 30 + kThreadBits ? kTailCap + 8 : 30 + kThreadBits;
1430 const size_t fold_count = count / 2 + count % 2;
1431 const size_t fold_blocks = fold_count / threads_per_block + (fold_count % threads_per_block != 0);
1432 if (fold_blocks >
static_cast<size_t>(INT_MAX))
return cudaErrorInvalidConfiguration;
1433 DeviceScratch first_storage(stream), second_storage(stream);
1434 cudaError_t status = first_storage.Allocate<Node>(count);
1435 if (status != cudaSuccess)
return status;
1436 status = second_storage.Allocate<Node>(second_count);
1437 if (status != cudaSuccess)
return status;
1438 auto *nodes = first_storage.Get<Node>();
1439 auto *next = second_storage.Get<Node>();
1440 status = fss::detail::ForEachAlignedRange<in_bits>(first, last, kChunkDepth,
1441 [&](In chunk_first,
int depth,
size_t offset,
size_t) {
1442 const int frontier = depth < frontier_depth ? depth : frontier_depth;
1443 const int thread_bits = frontier < kThreadBits ? frontier : kThreadBits;
1444 const int block_root_depth = frontier - thread_bits;
1445 const size_t blocks =
size_t{1} << block_root_depth;
1446 if (blocks >
static_cast<size_t>(INT_MAX))
return cudaErrorInvalidConfiguration;
1447 VdpfRangeMerkleLeavesDeviceKeyKernel<in_bits, threads_per_block, Group, Prg, XorHash, Hash, In>
1448 <<<blocks, 1 << thread_bits, 0, stream>>>(b, chunk_first, depth,
1449 frontier, block_root_depth, s0, cws, tr, stride, cs,
1450 ys + offset, nodes + offset, prg, xor_hash, hash);
1451 return cudaGetLastError();
1453 if (status != cudaSuccess)
return status;
1454 return VdpfFoldNodesToProof<threads_per_block>(nodes, next, count, cs, pi, hash,
1455 stream, &first_storage, &second_storage);
1458template <
int in_bits,
typename Group,
typename Prg,
typename XorHash,
typename Hash,
1459 typename In,
int threads_per_block = 256>
1460__host__
inline cudaError_t VdpfBatchEvalMerkleGpuDeviceKey(
bool b,
const int4 *s0,
1461 const int4 *cws,
size_t cws_count,
const In *tr,
size_t stride,
const int4 *cs,
1462 const In *xs,
size_t xs_count, int4 *ys,
size_t ys_count, int4 *pi,
const Prg &prg,
1463 const XorHash &xor_hash,
const Hash &hash, cudaStream_t stream) {
1464 static_assert(1 <= threads_per_block && threads_per_block <= 1024 &&
1465 (threads_per_block & (threads_per_block - 1)) == 0,
1466 "gpu block size must be a power of two in [1,1024]");
1467 using Node = cuda::std::array<int4, 2>;
1468 constexpr size_t kMaxElements = SIZE_MAX /
sizeof(int4);
1469 const size_t count = xs_count;
1470 if (count == 0 || ys_count < count || stride == 0 ||
1471 stride > (kMaxElements - 1) /
static_cast<size_t>(in_bits) ||
1472 cws_count <
static_cast<size_t>(in_bits) * stride + 1 ||
1473 s0 ==
nullptr || cws ==
nullptr || tr ==
nullptr || cs ==
nullptr ||
1474 xs ==
nullptr || ys ==
nullptr || pi ==
nullptr)
return cudaErrorInvalidValue;
1475 const size_t second_count = count / 2 + count % 2;
1476 constexpr size_t kMaxNodes = size_t(-1) /
sizeof(Node);
1477 if (count > kMaxNodes || second_count > kMaxNodes - count)
return cudaErrorInvalidValue;
1478 const size_t leaf_blocks = count / threads_per_block + (count % threads_per_block != 0);
1479 if (leaf_blocks >
static_cast<size_t>(INT_MAX))
return cudaErrorInvalidConfiguration;
1480 DeviceScratch first_storage(stream), second_storage(stream);
1481 cudaError_t status = first_storage.Allocate<Node>(count);
1482 if (status != cudaSuccess)
return status;
1483 status = second_storage.Allocate<Node>(second_count);
1484 if (status != cudaSuccess)
return status;
1485 auto *nodes = first_storage.Get<Node>();
1486 auto *next = second_storage.Get<Node>();
1487 VdpfBatchMerkleLeavesDeviceKeyKernel<in_bits, Group, Prg, XorHash, Hash, In>
1488 <<<
static_cast<int>(leaf_blocks), threads_per_block, 0, stream>>>(
1489 b, s0, cws, tr, stride, cs, xs, ys, nodes, count, prg, xor_hash, hash);
1490 status = cudaGetLastError();
1491 if (status != cudaSuccess)
return status;
1492 return VdpfFoldNodesToProof<threads_per_block>(nodes, next, count, cs, pi, hash,
1493 stream, &first_storage, &second_storage);
1500template <
int in_bits,
typename Group,
typename Prg,
typename XorHash,
typename Hash,
typename In>
1502 Groupable<Group> && Prgable<Prg, 2> && XorHashable<XorHash> && Hashable<Hash>)
1504 : prg_(prg), xor_hash_(xor_hash), hash_(hash) {}
1506template <
int in_bits,
typename Group,
typename Prg,
typename XorHash,
typename Hash,
typename In>
1510 bool b, int4 s0, cuda::std::span<const int4> cws, In tr,
1511 cuda::std::span<const int4, 4> cs, In x, int4 &y,
size_t stride) {
1513 constexpr size_t kMaxElements = SIZE_MAX /
sizeof(int4);
1514 util::RequireArg(stride <= (kMaxElements - 1) /
static_cast<size_t>(in_bits),
1515 "Vdpf::Eval: stride exceeds the correction array limit");
1516 const size_t required =
static_cast<size_t>(in_bits) * stride + 1;
1518 "Vdpf::Eval: cws must hold in_bits*stride+1 elements");
1519 return EvalCws(b, s0, CwsView{cws.data(), tr, stride}, cs, x, y);
1522template <
int in_bits,
typename Group,
typename Prg,
typename XorHash,
typename Hash,
typename In>
1525template <typename Cws>
1526inline __host__ __device__ cuda::std::array<int4, 4> Vdpf<in_bits, Group, Prg, XorHash, Hash, In>::EvalCws(
1527 bool b, int4 s0, Cws cws, cuda::std::span<const int4, 4> cs, In x, int4 &y) {
1532 for (
int i = 0; i < in_bits; ++i) {
1539 bool x_bit = (x >> (in_bits - 1 - i)) & 1;
1540 auto [child] = detail::GenHalf<2>(prg_, s, x_bit);
1545 child_t = child_t ^ (x_bit ? tr_cw : tl_cw);
1552 auto g = Group::From(s);
1553 int4 ocw = cws[in_bits].s;
1555 "Vdpf::Eval: the terminal group value must have a zero LSB");
1556 if (t) g = g + Group::From(ocw);
1560 int4 x_buf = fss::detail::Pack(x);
1562 auto pi_tilde = xor_hash_.Hash(cuda::std::tuple<int4, const int4>{x_buf, s});
1564 pi_tilde =
util::Xor(cuda::std::span<const int4, 4>(pi_tilde), cuda::std::span<const int4, 4>(cs));
1570template <
int in_bits,
typename Group,
typename Prg,
typename XorHash,
typename Hash,
typename In>
1572 Groupable<Group> && Prgable<Prg, 2> && XorHashable<XorHash> && Hashable<Hash>)
1574 cuda::std::span<
const cuda::std::array<int4, 4>> pi_tildes, cuda::std::span<const int4, 4> cs,
1575 cuda::std::span<int4, 4> pi) {
1580 for (
size_t i = 0; i < pi_tildes.size(); ++i) {
1581 cuda::std::array<int4, 4> h_input =
1582 util::Xor(cuda::std::span<const int4, 4>(pi), cuda::std::span<const int4, 4>(pi_tildes[i]));
1583 auto h_out = hash_.Hash(cuda::std::span<const int4, 4>(h_input));
1589template <
int in_bits,
typename Group,
typename Prg,
typename XorHash,
typename Hash,
typename In>
1593 cuda::std::span<const int4, 4> pi0, cuda::std::span<const int4, 4> pi1) {
1594 for (
int i = 0; i < 4; ++i) {
1595 if (pi0[i].x != pi1[i].x || pi0[i].y != pi1[i].y || pi0[i].z != pi1[i].z || pi0[i].w != pi1[i].w)
return false;
1600template <
int in_bits,
typename Group,
typename Prg,
typename XorHash,
typename Hash,
typename In>
1604 bool b, int4 s0, cuda::std::span<const int4> cws, In tr, cuda::std::span<const int4, 4> cs,
1605 cuda::std::span<int4> ys, cuda::std::span<int4, 4> pi,
int par_depth,
size_t stride) {
1606 assert(par_depth >= 0);
1607 constexpr size_t kOutputCount = detail::FullDomainSize<int4, in_bits>();
1608 EvalRange(b, In{0},
static_cast<In
>(kOutputCount - 1), s0, cws, tr, cs, ys, pi, par_depth, stride);
1611template <
int in_bits,
typename Group,
typename Prg,
typename XorHash,
typename Hash,
typename In>
1615 bool b, In first, In last, int4 s0, cuda::std::span<const int4> cws, In tr,
1616 cuda::std::span<const int4, 4> cs, cuda::std::span<int4> ys,
1617 cuda::std::span<int4, 4> pi,
int par_depth,
size_t stride) {
1618 if (stride == 0)
throw std::invalid_argument(
"Vdpf::EvalRange: stride must be positive");
1619 constexpr size_t kMaxElements = SIZE_MAX /
sizeof(int4);
1620 if (stride > (kMaxElements - 1) /
static_cast<size_t>(in_bits))
1621 throw std::invalid_argument(
"Vdpf::EvalRange: stride exceeds the correction array limit");
1622 const size_t required =
static_cast<size_t>(in_bits) * stride + 1;
1623 if (cws.size() < required)
1624 throw std::invalid_argument(
"Vdpf::EvalRange: cws must hold in_bits*stride+1 elements");
1625 EvalRangeCws(b, first, last, s0, CwsView{cws.data(), tr, stride}, cs, ys, pi, par_depth);
1628template <
int in_bits,
typename Group,
typename Prg,
typename XorHash,
typename Hash,
typename In>
1631template <typename Cws>
1632inline void Vdpf<in_bits, Group, Prg, XorHash, Hash, In>::EvalRangeCws(
1633 bool b, In first, In last, int4 s0, Cws cws, cuda::std::span<const int4, 4> cs,
1634 cuda::std::span<int4> ys, cuda::std::span<int4, 4> pi,
int par_depth) {
1635 assert(par_depth >= 0);
1636 In max_input =
static_cast<In
>(~In{0});
1637 if constexpr (in_bits <
sizeof(In) * 8) {
1638 max_input =
static_cast<In
>((In{1} << in_bits) - 1);
1641 throw std::invalid_argument(
"Vdpf::EvalRange: first must not exceed last");
1642 if (last > max_input)
1643 throw std::invalid_argument(
"Vdpf::EvalRange: last must stay inside the input domain");
1644 const __uint128_t distance =
static_cast<__uint128_t
>(last) - first;
1645 if (distance >=
static_cast<size_t>(-1) /
sizeof(int4))
1646 throw std::invalid_argument(
"Vdpf::EvalRange: the interval is too large for size_t");
1647 const size_t num_inputs =
static_cast<size_t>(distance) + 1;
1648 if (ys.size() < num_inputs)
1649 throw std::invalid_argument(
"Vdpf::EvalRange: ys must hold last-first+1 entries");
1654 int4 ocw = cws[in_bits].s;
1656 "Vdpf::EvalRange: the terminal group value must have a zero LSB");
1657 auto ocw_group = Group::From(ocw);
1660 const int par_depth_ = fss::detail::ResolveParDepth(par_depth, in_bits);
1663 if (par_depth_ == 0) {
1664 EvalPrunedTree(st, cws, ys, In{0}, max_input, 0, 0, first, last);
1666 const int threads = fss::detail::ResolveParThreads(par_depth_, num_inputs);
1668#pragma omp parallel num_threads(threads)
1670 EvalPrunedTree(st, cws, ys, In{0}, max_input, 0, par_depth_, first, last);
1678 for (
size_t offset = 0; offset < num_inputs; ++offset) {
1679 const In x =
static_cast<In
>(first + offset);
1680 int4 sj = ys[offset];
1685 auto g = Group::From(sj);
1686 if (tj) g = g + ocw_group;
1688 ys[offset] = g.Into();
1691 int4 x_buf = fss::detail::Pack(x);
1693 auto pi_tilde = xor_hash_.Hash(cuda::std::tuple<int4, const int4>{x_buf, sj});
1695 pi_tilde =
util::Xor(cuda::std::span<const int4, 4>(pi_tilde), cuda::std::span<const int4, 4>(cs));
1698 cuda::std::array<int4, 4> h_input =
1699 util::Xor(cuda::std::span<const int4, 4>(pi), cuda::std::span<const int4, 4>(pi_tilde));
1700 auto h_out = hash_.Hash(cuda::std::span<const int4, 4>(h_input));
1706template <
int in_bits,
typename Group,
typename Prg,
typename XorHash,
typename Hash,
typename In>
1708 Groupable<Group> && Prgable<Prg, 2> && XorHashable<XorHash> && Hashable<Hash>)
1710 cuda::std::span<
const cuda::std::array<int4, 4>> pi_tildes, cuda::std::span<const int4, 4> cs,
1711 cuda::std::span<int4, 4> pi,
int par_depth) {
1712 assert(par_depth >= 0);
1713 size_t count = pi_tildes.size();
1715 throw std::invalid_argument(
"Vdpf::ProveMerkle: pi_tildes must not be empty");
1717 std::vector<cuda::std::array<int4, 2>> nodes(count);
1719 const size_t groups = count / 4;
1720 fss::detail::ParallelFor(par_depth, groups, [&](
size_t g) {
1722 hash_.Hash4(cuda::std::span<
const cuda::std::array<int4, 4>, 4>(pi_tildes.data() + 4 * g, 4));
1723 for (
size_t j = 0; j < 4; ++j) nodes[4 * g + j] = out[j];
1725 for (
size_t i = 4 * groups; i < count; ++i) {
1726 nodes[i] = hash_.Hash(cuda::std::span<const int4, 4>(pi_tildes[i]));
1729 fss::detail::ParallelFor(par_depth, count, [&](
size_t i) {
1730 nodes[i] = hash_.Hash(cuda::std::span<const int4, 4>(pi_tildes[i]));
1735 const size_t next_count = count / 2 + count % 2;
1736 std::vector<cuda::std::array<int4, 2>> next(next_count);
1738 const size_t pairs = count / 2;
1739 const size_t groups = pairs / 4;
1740 fss::detail::ParallelFor(par_depth, groups, [&](
size_t g) {
1741 cuda::std::array<cuda::std::array<int4, 4>, 4> msgs;
1742 for (
size_t j = 0; j < 4; ++j) {
1743 const auto *p = nodes.data() + 8 * g + 2 * j;
1744 msgs[j] = {p[0][0], p[0][1], p[1][0], p[1][1]};
1746 auto out = hash_.Hash4(cuda::std::span<
const cuda::std::array<int4, 4>, 4>(msgs));
1747 for (
size_t j = 0; j < 4; ++j) next[4 * g + j] = out[j];
1750 for (
size_t i = 4 * groups; i < next_count; ++i) {
1751 next[i] = detail::VdpfMerkleFoldNode(hash_, nodes.data(), count, i);
1754 fss::detail::ParallelFor(par_depth, next_count, [&](
size_t i) {
1755 next[i] = detail::VdpfMerkleFoldNode(hash_, nodes.data(), count, i);
1758 nodes = std::move(next);
1759 count = nodes.size();
1762 pi[0] = nodes[0][0];
1763 pi[1] = nodes[0][1];
1768template <
int in_bits,
typename Group,
typename Prg,
typename XorHash,
typename Hash,
typename In>
1772 bool b, int4 s0, cuda::std::span<const int4> cws, In tr, cuda::std::span<const int4, 4> cs,
1773 cuda::std::span<int4> ys, cuda::std::span<int4, 4> pi,
int par_depth,
size_t stride) {
1774 constexpr size_t kOutputCount = detail::FullDomainSize<cuda::std::array<int4, 4>, in_bits>();
1775 EvalRangeMerkle(b, In{0},
static_cast<In
>(kOutputCount - 1), s0, cws, tr, cs, ys, pi, par_depth, stride);
1778template <
int in_bits,
typename Group,
typename Prg,
typename XorHash,
typename Hash,
typename In>
1782 bool b, In first, In last, int4 s0, cuda::std::span<const int4> cws, In tr,
1783 cuda::std::span<const int4, 4> cs, cuda::std::span<int4> ys,
1784 cuda::std::span<int4, 4> pi,
int par_depth,
size_t stride) {
1785 if (stride == 0)
throw std::invalid_argument(
"Vdpf::EvalRangeMerkle: stride must be positive");
1786 constexpr size_t kMaxElements = SIZE_MAX /
sizeof(int4);
1787 if (stride > (kMaxElements - 1) /
static_cast<size_t>(in_bits))
1788 throw std::invalid_argument(
"Vdpf::EvalRangeMerkle: stride exceeds the correction array limit");
1789 const size_t required =
static_cast<size_t>(in_bits) * stride + 1;
1790 if (cws.size() < required)
1791 throw std::invalid_argument(
"Vdpf::EvalRangeMerkle: cws must hold in_bits*stride+1 elements");
1792 EvalRangeMerkleCws(b, first, last, s0, CwsView{cws.data(), tr, stride}, cs, ys, pi, par_depth);
1795template <
int in_bits,
typename Group,
typename Prg,
typename XorHash,
typename Hash,
typename In>
1798template <typename Cws>
1799inline void Vdpf<in_bits, Group, Prg, XorHash, Hash, In>::EvalRangeMerkleCws(
1800 bool b, In first, In last, int4 s0, Cws cws,
1801 cuda::std::span<const int4, 4> cs, cuda::std::span<int4> ys,
1802 cuda::std::span<int4, 4> pi,
int par_depth) {
1803 assert(par_depth >= 0);
1804 In max_input =
static_cast<In
>(~In{0});
1805 if constexpr (in_bits <
sizeof(In) * 8) {
1806 max_input =
static_cast<In
>((In{1} << in_bits) - 1);
1809 throw std::invalid_argument(
"Vdpf::EvalRangeMerkle: first must not exceed last");
1810 if (last > max_input)
1811 throw std::invalid_argument(
"Vdpf::EvalRangeMerkle: last must stay inside the input domain");
1812 const __uint128_t distance =
static_cast<__uint128_t
>(last) - first;
1814 constexpr size_t kMaxBytes = size_t(-1);
1815 if (distance >= kMaxBytes /
sizeof(cuda::std::array<int4, 4>))
1816 throw std::invalid_argument(
"Vdpf::EvalRangeMerkle: the interval is too large for size_t");
1817 const size_t kOutputCount =
static_cast<size_t>(distance) + 1;
1818 const size_t next_count = kOutputCount / 2 + kOutputCount % 2;
1819 if (kOutputCount > kMaxBytes / (
sizeof(cuda::std::array<int4, 4>) +
sizeof(cuda::std::array<int4, 2>)))
1820 throw std::invalid_argument(
"Vdpf::EvalRangeMerkle: the proof scratch is too large for size_t");
1821 const size_t initial_bytes = kOutputCount *
1822 (
sizeof(cuda::std::array<int4, 4>) +
sizeof(cuda::std::array<int4, 2>));
1823 if (next_count > (kMaxBytes - initial_bytes) /
sizeof(cuda::std::array<int4, 2>))
1824 throw std::invalid_argument(
"Vdpf::EvalRangeMerkle: the proof scratch is too large for size_t");
1825 if (ys.size() < kOutputCount)
1826 throw std::invalid_argument(
"Vdpf::EvalRangeMerkle: ys must hold last-first+1 entries");
1831 int4 ocw = cws[in_bits].s;
1833 "Vdpf::EvalRangeMerkle: the terminal group value must have a zero LSB");
1834 auto ocw_group = Group::From(ocw);
1837 int par_depth_ = fss::detail::ResolveParDepth(par_depth, in_bits);
1840 if (par_depth_ == 0) {
1841 EvalPrunedTree(st, cws, ys, In{0}, max_input, 0, 0, first, last);
1843 int threads = fss::detail::ResolveParThreads(par_depth_, kOutputCount);
1844#pragma omp parallel num_threads(threads)
1846 EvalPrunedTree(st, cws, ys, In{0}, max_input, 0, par_depth_, first, last);
1849 const size_t n = kOutputCount;
1850 std::vector<cuda::std::array<int4, 4>> leaves(n);
1851 fss::detail::ParallelFor(par_depth, n, [&](
size_t j) {
1856 auto g = Group::From(sj);
1857 if (tj) g = g + ocw_group;
1861 int4 x_buf = fss::detail::Pack(
static_cast<In
>(first + j));
1863 auto pi_tilde = xor_hash_.Hash(cuda::std::tuple<int4, const int4>{x_buf, sj});
1865 leaves[j] =
util::Xor(cuda::std::span<const int4, 4>(pi_tilde), cuda::std::span<const int4, 4>(cs));
1867 leaves[j] = pi_tilde;
1870 ProveMerkle(cuda::std::span<
const cuda::std::array<int4, 4>>(leaves), cuda::std::span<const int4, 4>(cs), pi, par_depth);
1873template <
int in_bits,
typename Group,
typename Prg,
typename XorHash,
typename Hash,
typename In>
1875 Groupable<Group> && Prgable<Prg, 2> && XorHashable<XorHash> && Hashable<Hash>)
1877 bool b, int4 s0, cuda::std::span<const int4> cws, In tr,
1878 cuda::std::span<const int4, 4> cs, cuda::std::span<const In> xs,
1879 cuda::std::span<int4> ys, cuda::std::span<int4, 4> pi,
size_t stride) {
1880 if (stride == 0)
throw std::invalid_argument(
"Vdpf::BatchEval: stride must be positive");
1881 constexpr size_t kMaxElements = SIZE_MAX /
sizeof(int4);
1882 if (stride > (kMaxElements - 1) /
static_cast<size_t>(in_bits))
1883 throw std::invalid_argument(
"Vdpf::BatchEval: stride exceeds the correction array limit");
1884 const size_t required =
static_cast<size_t>(in_bits) * stride + 1;
1885 if (cws.size() < required)
1886 throw std::invalid_argument(
"Vdpf::BatchEval: cws must hold in_bits*stride+1 elements");
1887 EvalBatchCws<false>(b, s0, CwsView{cws.data(), tr, stride}, cs, xs, ys, pi);
1890template <
int in_bits,
typename Group,
typename Prg,
typename XorHash,
typename Hash,
typename In>
1893template <bool merkle, typename Cws>
1894inline void Vdpf<in_bits, Group, Prg, XorHash, Hash, In>::EvalBatchCws(
1895 bool b, int4 s0, Cws cws, cuda::std::span<const int4, 4> cs, cuda::std::span<const In> xs,
1896 cuda::std::span<int4> ys, cuda::std::span<int4, 4> pi) {
1898 throw std::invalid_argument(merkle ?
"Vdpf::BatchEvalMerkle: xs must not be empty"
1899 :
"Vdpf::BatchEval: xs must not be empty");
1900 if (ys.size() < xs.size())
1901 throw std::invalid_argument(merkle ?
"Vdpf::BatchEvalMerkle: ys must hold xs.size() entries"
1902 :
"Vdpf::BatchEval: ys must hold xs.size() entries");
1903 std::vector<cuda::std::array<int4, 4>> pi_tildes(xs.size());
1904 for (
size_t i = 0; i < xs.size(); ++i) {
1905 pi_tildes[i] = EvalCws(b, s0, cws, cs, xs[i], ys[i]);
1907 if constexpr (merkle) {
1908 ProveMerkle(cuda::std::span<
const cuda::std::array<int4, 4>>(pi_tildes), cs, pi);
1910 Prove(cuda::std::span<
const cuda::std::array<int4, 4>>(pi_tildes), cs, pi);
1914template <
int in_bits,
typename Group,
typename Prg,
typename XorHash,
typename Hash,
typename In>
1916 Groupable<Group> && Prgable<Prg, 2> && XorHashable<XorHash> && Hashable<Hash>)
1918 bool b, int4 s0, cuda::std::span<const int4> cws, In tr,
1919 cuda::std::span<const int4, 4> cs, cuda::std::span<const In> xs,
1920 cuda::std::span<int4> ys, cuda::std::span<int4, 4> pi,
size_t stride) {
1921 if (stride == 0)
throw std::invalid_argument(
"Vdpf::BatchEvalMerkle: stride must be positive");
1922 constexpr size_t kMaxElements = SIZE_MAX /
sizeof(int4);
1923 if (stride > (kMaxElements - 1) /
static_cast<size_t>(in_bits))
1924 throw std::invalid_argument(
"Vdpf::BatchEvalMerkle: stride exceeds the correction array limit");
1925 const size_t required =
static_cast<size_t>(in_bits) * stride + 1;
1926 if (cws.size() < required)
1927 throw std::invalid_argument(
"Vdpf::BatchEvalMerkle: cws must hold in_bits*stride+1 elements");
1928 EvalBatchCws<true>(b, s0, CwsView{cws.data(), tr, stride}, cs, xs, ys, pi);
1931template <
int in_bits,
typename Group,
typename Prg,
typename XorHash,
typename Hash,
typename In>
1935 cuda::std::span<int4> cws, In &tr, cuda::std::span<int4, 4> cs,
1936 cuda::std::span<const int4, 2> s0s, In a, int4 b_buf,
size_t stride) {
1938 constexpr size_t kMaxElements = SIZE_MAX /
sizeof(int4);
1939 util::RequireArg(stride <= (kMaxElements - 1) /
static_cast<size_t>(in_bits),
1940 "Vdpf::Gen: stride exceeds the correction array limit");
1941 const size_t required =
static_cast<size_t>(in_bits) * stride + 1;
1943 "Vdpf::Gen: cws must hold in_bits*stride+1 elements");
1944 return Generate(CwsWriter{cws.data(), stride, tr}, cs, s0s.data(), a, b_buf);
1947template <
int in_bits,
typename Group,
typename Prg,
typename XorHash,
typename Hash,
typename In>
1950template <typename Writer>
1951inline __host__ __device__
int Vdpf<in_bits, Group, Prg, XorHash, Hash, In>::Generate(
1952 Writer writer, cuda::std::span<int4, 4> cs,
const int4 *s0s, In a, int4 b_buf) {
1961 for (
int i = 0; i < in_bits; ++i) {
1962 auto [s0l, s0r] = prg_.Gen(s0);
1963 auto [s1l, s1r] = prg_.Gen(s1);
1974 bool a_bit = (a >> (in_bits - 1 - i)) & 1;
1980 bool tl_cw = t0l ^ t1l ^ a_bit ^ 1;
1981 bool tr_cw = t0r ^ t1r ^ a_bit;
1989 if (t0) t0 = t0l ^ tl_cw;
1991 if (t1) t1 = t1l ^ tl_cw;
1999 if (t0) t0 = t0r ^ tr_cw;
2001 if (t1) t1 = t1r ^ tr_cw;
2006 writer.Write(i, s_cw, tr_cw);
2010 int4 a_buf = fss::detail::Pack(a);
2012 auto pi_tilde_0 = xor_hash_.Hash(cuda::std::tuple<int4, const int4>{a_buf, s0});
2013 auto pi_tilde_1 = xor_hash_.Hash(cuda::std::tuple<int4, const int4>{a_buf, s1});
2014 for (
int i = 0; i < 4; ++i) cs[i] =
util::Xor(pi_tilde_0[i], pi_tilde_1[i]);
2018 if ((cs[0].x | cs[0].y | cs[0].z | cs[0].w | cs[1].x | cs[1].y | cs[1].z | cs[1].w | cs[2].x | cs[2].y |
2019 cs[2].z | cs[2].w | cs[3].x | cs[3].y | cs[3].z | cs[3].w) == 0) {
2024 if (t0 == t1)
return 1;
2027 auto v_cw = Group::From(b_buf) + (-Group::From(s0)) + Group::From(s1);
2028 if (t1) v_cw = -v_cw;
2029 writer.Finish(v_cw.Into());
2034template <
int in_bits,
typename Group,
typename Prg,
typename XorHash,
typename Hash,
typename In>
2036 Groupable<Group> && Prgable<Prg, 2> && XorHashable<XorHash> && Hashable<Hash>)
2037template <typename Cws>
2038inline void Vdpf<in_bits, Group, Prg, XorHash, Hash, In>::EvalTree(
2039 int4 st, Cws cws, cuda::std::span<int4> ys,
size_t l,
size_t r,
int i,
int par_depth_) {
2056 auto [sl, sr] = prg_.Gen(s);
2075 size_t mid = (l + r) / 2;
2077 if (i < par_depth_) {
2079 EvalTree(stl, cws, ys, l, mid, i + 1, par_depth_);
2081 EvalTree(str, cws, ys, mid, r, i + 1, par_depth_);
2084 EvalTree(stl, cws, ys, l, mid, i + 1, par_depth_);
2085 EvalTree(str, cws, ys, mid, r, i + 1, par_depth_);
2089template <
int in_bits,
typename Group,
typename Prg,
typename XorHash,
typename Hash,
typename In>
2091 Groupable<Group> && Prgable<Prg, 2> && XorHashable<XorHash> && Hashable<Hash>)
2092template <typename Cws>
2093inline void Vdpf<in_bits, Group, Prg, XorHash, Hash, In>::EvalPrunedTree(
2094 int4 st, Cws cws, cuda::std::span<int4> ys, In l, In r,
int i,
2095 int par_depth_, In first, In last) {
2097 if (r < first || l > last) {
2101 if (l >= first && r <= last) {
2102 const size_t offset =
static_cast<size_t>(l - first);
2103 const size_t count =
static_cast<size_t>(r - l) + 1;
2104 EvalTree(st, cws, ys.subspan(offset, count), 0, count, i, par_depth_);
2110 ys[
static_cast<size_t>(l - first)] = st;
2124 auto [sl, sr] = prg_.Gen(s);
2143 const In mid =
static_cast<In
>(l + (r - l) / 2);
2145 if (i < par_depth_) {
2148 EvalPrunedTree(stl, cws, ys, l, mid, i + 1, par_depth_, first, last);
2152 EvalPrunedTree(str, cws, ys,
static_cast<In
>(mid + 1), r, i + 1, par_depth_, first, last);
2157 EvalPrunedTree(stl, cws, ys, l, mid, i + 1, par_depth_, first, last);
2160 EvalPrunedTree(str, cws, ys,
static_cast<In
>(mid + 1), r, i + 1, par_depth_, first, last);
2165template <
int in_bits,
typename Group,
typename Prg,
typename XorHash,
typename Hash,
typename In>
2167 Groupable<Group> && Prgable<Prg, 2> && XorHashable<XorHash> && Hashable<Hash>)
2168template <int frontier_depth, int threads_per_block>
2170struct Vdpf<in_bits, Group, Prg, XorHash, Hash, In>::GpuLaunchShape {
2171 static_assert(frontier_depth >= 0,
"gpu frontier depth must be nonnegative");
2172 static_assert(1 <= threads_per_block && threads_per_block <= 1024 &&
2173 (threads_per_block & (threads_per_block - 1)) == 0,
2174 "gpu block size must be a power of two in [1,1024]");
2175 static constexpr int kFrontierDepth = frontier_depth < in_bits ? frontier_depth : in_bits;
2176 static_assert(in_bits - kFrontierDepth <= 8,
2177 "gpu frontier leaves more than eight tail levels; specify a larger frontier_depth");
2178 static constexpr int kThreadsPerBlockLog2 = [] {
2179 int requested_log2 = 0;
2180 for (
int threads = threads_per_block; threads > 1; threads >>= 1) ++requested_log2;
2181 return requested_log2 < kFrontierDepth ? requested_log2 : kFrontierDepth;
2183 static constexpr int kThreadsPerBlock = 1 << kThreadsPerBlockLog2;
2184 static constexpr int kBlockRootDepth = kFrontierDepth - kThreadsPerBlockLog2;
2185 static_assert(kBlockRootDepth <= 30,
"gpu block index exceeds the signed-int range");
2186 static constexpr size_t kBlockCount =
size_t{1} << (kBlockRootDepth <= 30 ? kBlockRootDepth : 0);
2187 static_assert(kBlockCount <= kThreadsPerBlock,
2188 "merkle block roots must fit within one final block");
2191template <
int in_bits,
typename Group,
typename Prg,
typename XorHash,
typename Hash,
typename In>
2193 Groupable<Group> && Prgable<Prg, 2> && XorHashable<XorHash> && Hashable<Hash>)
2194template <int frontier_depth, int threads_per_block>
2196 bool b, int4 s0, cuda::std::span<const int4> cws, In tr, cuda::std::span<const int4, 4> cs,
2197 cuda::std::span<int4> ys, cuda::std::span<int4, 4> pi, cudaStream_t stream,
size_t stride)
const
2198 requires(in_bits <
sizeof(size_t) * 8) {
2199#if defined(__CUDACC__)
2200 using LaunchShape = GpuLaunchShape<frontier_depth, threads_per_block>;
2201 constexpr size_t kOutputCount =
size_t{1} << in_bits;
2202 constexpr size_t kMaxElements = SIZE_MAX /
sizeof(int4);
2203 if (stride == 0 || stride > (kMaxElements - 1) /
static_cast<size_t>(in_bits) ||
2204 cws.size() <
static_cast<size_t>(in_bits) * stride + 1 || ys.size() < kOutputCount ||
2205 !detail::FitsElementCount<int4>(kOutputCount) ||
2206 !detail::FitsElementCount<cuda::std::array<int4, 2>>(LaunchShape::kBlockCount) ||
2207 cws.data() ==
nullptr || cs.data() ==
nullptr || ys.data() ==
nullptr ||
2208 pi.data() ==
nullptr)
return cudaErrorInvalidValue;
2209 gpu::detail::DeviceScratch roots_storage(stream);
2210 cudaError_t status = roots_storage.Allocate<cuda::std::array<int4, 2>>(LaunchShape::kBlockCount);
2211 if (status != cudaSuccess)
return status;
2212 auto *block_roots = roots_storage.Get<cuda::std::array<int4, 2>>();
2213 gpu::detail::VdpfMerkleFrontierKernel<in_bits, LaunchShape::kFrontierDepth,
2214 LaunchShape::kBlockRootDepth, Group, Prg, XorHash, Hash, In>
2215 <<<LaunchShape::kBlockCount, LaunchShape::kThreadsPerBlock, 0, stream>>>(
2216 b, s0, cws.data(), tr, stride, cs.data(), ys.data(), block_roots, prg_, xor_hash_, hash_);
2217 status = cudaGetLastError();
2218 if (status != cudaSuccess)
return status;
2219 gpu::detail::VdpfMerkleFinalizeKernel<LaunchShape::kThreadsPerBlock, Hash>
2220 <<<1, LaunchShape::kThreadsPerBlock, 0, stream>>>(
2221 block_roots,
static_cast<int>(LaunchShape::kBlockCount), cs.data(), pi.data(), hash_);
2222 return gpu::detail::ReleaseScratch(cudaGetLastError(), {&roots_storage});
2224 static_assert(
sizeof(Prg) == 0,
"gpu launch requires a CUDA compiler");
2225 return cudaErrorNotSupported;
2229template <
int in_bits,
typename Group,
typename Prg,
typename XorHash,
typename Hash,
typename In>
2232template <int frontier_depth, int threads_per_block>
2234 bool b, In first, In last, int4 s0, cuda::std::span<const int4> cws, In tr,
2235 cuda::std::span<const int4, 4> cs, cuda::std::span<int4> ys,
2236 cuda::std::span<int4, 4> pi, cudaStream_t stream,
size_t stride)
const {
2237 static_assert(frontier_depth >= 0,
"gpu frontier depth must be nonnegative");
2238 static_assert(1 <= threads_per_block && threads_per_block <= 1024 &&
2239 (threads_per_block & (threads_per_block - 1)) == 0,
2240 "gpu block size must be a power of two in [1,1024]");
2241#if defined(__CUDACC__)
2242 using Node = cuda::std::array<int4, 2>;
2243 constexpr size_t kMaxElements = SIZE_MAX /
sizeof(int4);
2245 if (!detail::RangeOutputCount<in_bits>(first, last, ys.size(), count) || stride == 0 ||
2246 stride > (kMaxElements - 1) /
static_cast<size_t>(in_bits) ||
2247 cws.size() <
static_cast<size_t>(in_bits) * stride + 1 ||
2248 cws.data() ==
nullptr || cs.data() ==
nullptr ||
2249 ys.data() ==
nullptr || pi.data() ==
nullptr)
return cudaErrorInvalidValue;
2250 const size_t second_count = count / 2 + count % 2;
2251 constexpr size_t kMaxNodes = size_t(-1) /
sizeof(Node);
2252 if (count > kMaxNodes || second_count > kMaxNodes - count)
return cudaErrorInvalidValue;
2253 constexpr int kThreadBits = [] {
2255 for (
int threads = threads_per_block; threads > 1; threads >>= 1) ++bits;
2258 constexpr int kTailCap = frontier_depth < 30 + kThreadBits ? frontier_depth : 30 + kThreadBits;
2259 constexpr int kChunkDepth = kTailCap + 8 < 30 + kThreadBits ? kTailCap + 8 : 30 + kThreadBits;
2260 const size_t fold_count = count / 2 + count % 2;
2261 const size_t fold_blocks = fold_count / threads_per_block + (fold_count % threads_per_block != 0);
2262 if (fold_blocks >
static_cast<size_t>(INT_MAX))
return cudaErrorInvalidConfiguration;
2263 gpu::detail::DeviceScratch first_storage(stream), second_storage(stream);
2264 cudaError_t status = first_storage.Allocate<Node>(count);
2265 if (status != cudaSuccess)
return status;
2266 status = second_storage.Allocate<Node>(second_count);
2267 if (status != cudaSuccess)
return status;
2268 auto *nodes = first_storage.Get<Node>();
2269 auto *next = second_storage.Get<Node>();
2270 status = detail::ForEachAlignedRange<in_bits>(first, last, kChunkDepth,
2271 [&](In chunk_first,
int depth,
size_t offset,
size_t) {
2272 const int frontier = depth < frontier_depth ? depth : frontier_depth;
2273 const int thread_bits = frontier < kThreadBits ? frontier : kThreadBits;
2274 const int block_root_depth = frontier - thread_bits;
2275 const size_t blocks =
size_t{1} << block_root_depth;
2276 if (blocks >
static_cast<size_t>(INT_MAX))
return cudaErrorInvalidConfiguration;
2277 gpu::detail::VdpfRangeMerkleLeavesKernel<in_bits, threads_per_block, Group, Prg, XorHash, Hash, In>
2278 <<<blocks, 1 << thread_bits, 0, stream>>>(b, chunk_first, depth,
2279 frontier, block_root_depth, s0, cws.data(), tr, stride, cs.data(),
2280 ys.data() + offset, nodes + offset, prg_, xor_hash_, hash_);
2281 return cudaGetLastError();
2283 if (status != cudaSuccess)
return status;
2284 return gpu::detail::VdpfFoldNodesToProof<threads_per_block>(nodes, next, count, cs.data(),
2285 pi.data(), hash_, stream, &first_storage, &second_storage);
2287 static_assert(
sizeof(Prg) == 0,
"gpu launch requires a CUDA compiler");
2288 return cudaErrorNotSupported;
2292template <
int in_bits,
typename Group,
typename Prg,
typename XorHash,
typename Hash,
typename In>
2295template <int threads_per_block>
2297 bool b, int4 s0, cuda::std::span<const int4> cws, In tr,
2298 cuda::std::span<const int4, 4> cs, cuda::std::span<const In> xs,
2299 cuda::std::span<int4> ys, cuda::std::span<int4, 4> pi, cudaStream_t stream,
size_t stride)
const {
2300 static_assert(1 <= threads_per_block && threads_per_block <= 1024 &&
2301 (threads_per_block & (threads_per_block - 1)) == 0,
2302 "gpu block size must be a power of two in [1,1024]");
2303#if defined(__CUDACC__)
2304 using Node = cuda::std::array<int4, 2>;
2305 constexpr size_t kMaxElements = SIZE_MAX /
sizeof(int4);
2306 const size_t count = xs.size();
2307 if (count == 0 || ys.size() < count || stride == 0 ||
2308 stride > (kMaxElements - 1) /
static_cast<size_t>(in_bits) ||
2309 cws.size() <
static_cast<size_t>(in_bits) * stride + 1 ||
2310 cws.data() ==
nullptr || cs.data() ==
nullptr || xs.data() ==
nullptr ||
2311 ys.data() ==
nullptr || pi.data() ==
nullptr)
return cudaErrorInvalidValue;
2312 const size_t second_count = count / 2 + count % 2;
2313 constexpr size_t kMaxNodes = size_t(-1) /
sizeof(Node);
2314 if (count > kMaxNodes || second_count > kMaxNodes - count)
return cudaErrorInvalidValue;
2315 const size_t leaf_blocks = count / threads_per_block + (count % threads_per_block != 0);
2316 if (leaf_blocks >
static_cast<size_t>(INT_MAX))
return cudaErrorInvalidConfiguration;
2317 gpu::detail::DeviceScratch first_storage(stream), second_storage(stream);
2318 cudaError_t status = first_storage.Allocate<Node>(count);
2319 if (status != cudaSuccess)
return status;
2320 status = second_storage.Allocate<Node>(second_count);
2321 if (status != cudaSuccess)
return status;
2322 auto *nodes = first_storage.Get<Node>();
2323 auto *next = second_storage.Get<Node>();
2324 gpu::detail::VdpfBatchMerkleLeavesKernel<in_bits, Group, Prg, XorHash, Hash, In>
2325 <<<
static_cast<int>(leaf_blocks), threads_per_block, 0, stream>>>(
2326 b, s0, cws.data(), tr, stride, cs.data(), xs.data(), ys.data(), nodes, count,
2327 prg_, xor_hash_, hash_);
2328 status = cudaGetLastError();
2329 if (status != cudaSuccess)
return status;
2330 return gpu::detail::VdpfFoldNodesToProof<threads_per_block>(nodes, next, count, cs.data(),
2331 pi.data(), hash_, stream, &first_storage, &second_storage);
2333 static_assert(
sizeof(Prg) == 0,
"gpu launch requires a CUDA compiler");
2334 return cudaErrorNotSupported;
2338#if FSS_ENABLE_V1_COMPAT
2347template <
int in_bits,
typename Group,
typename Prg,
typename XorHash,
typename Hash,
typename In>
2351 Cw cws[], cuda::std::array<int4, 4> &cs, int4 &ocw,
2352 cuda::std::span<const int4, 2> s0s, In a, int4 b_buf) {
2353 int4 seed[in_bits + 1];
2355 const int res = Gen(cuda::std::span<int4>(seed, in_bits + 1), tr,
2356 cuda::std::span<int4, 4>(cs), s0s, a, b_buf);
2357 if (res != 0)
return res;
2358 for (
int i = 0; i < in_bits; ++i) cws[i] = {seed[i], ((tr >> i) & 1) != 0};
2359 ocw = seed[in_bits];
2363template <
int in_bits,
typename Group,
typename Prg,
typename XorHash,
typename Hash,
typename In>
2365 Groupable<Group> && Prgable<Prg, 2> && XorHashable<XorHash> && Hashable<Hash>)
2367 bool b, int4 s0, cuda::std::span<const Cw> cws, cuda::std::span<const int4, 4> cs,
2368 int4 ocw, In x, int4 &y) {
2369 int4 seed[in_bits + 1];
2371 for (
int i = 0; i < in_bits; ++i) {
2373 tr |=
static_cast<In
>(cws[i].tr) << i;
2375 seed[in_bits] = ocw;
2376 return Eval(b, s0, cuda::std::span<const int4>(seed, in_bits + 1), tr, cs, x, y);
2379template <
int in_bits,
typename Group,
typename Prg,
typename XorHash,
typename Hash,
typename In>
2381 Groupable<Group> && Prgable<Prg, 2> && XorHashable<XorHash> && Hashable<Hash>)
2383 bool b, int4 s0, cuda::std::span<const Cw> cws, cuda::std::span<const int4, 4> cs,
2384 int4 ocw, cuda::std::span<int4> ys, cuda::std::array<int4, 4> &pi) {
2385 int4 seed[in_bits + 1];
2387 for (
int i = 0; i < in_bits; ++i) {
2389 tr |=
static_cast<In
>(cws[i].tr) << i;
2391 seed[in_bits] = ocw;
2392 EvalAll(b, s0, cuda::std::span<const int4>(seed, in_bits + 1), tr, cs, ys, pi);
2399template <
int in_bits,
typename Group,
typename Prg,
typename XorHash,
typename Hash,
typename In>
2401 Groupable<Group> && Prgable<Prg, 2> && XorHashable<XorHash> && Hashable<Hash>)
2404 bool b,
const compat::detail::VdpfKeyRecord<Vdpf, Cw, in_bits> &key, In x) {
2405 int4 seed[in_bits + 1];
2407 for (
int i = 0; i < in_bits; ++i) {
2408 seed[i] = key.cws[i].s;
2409 tr |=
static_cast<In
>(key.cws[i].tr) << i;
2411 seed[in_bits] = key.ocw;
2412 compat::detail::EvalResultRecord<Vdpf> result;
2413 result.pi_tilde = Eval(b, key.s0, cuda::std::span<const int4>(seed, in_bits + 1), tr,
2414 cuda::std::span<const int4, 4>(key.cs), x, result.y);
2418template <
int in_bits,
typename Group,
typename Prg,
typename XorHash,
typename Hash,
typename In>
2420 Groupable<Group> && Prgable<Prg, 2> && XorHashable<XorHash> && Hashable<Hash>)
2422 bool b,
const compat::detail::VdpfKeyRecord<Vdpf, Cw, in_bits> &key,
2423 cuda::std::span<int4> ys, cuda::std::array<int4, 4> &pi) {
2424 int4 seed[in_bits + 1];
2426 for (
int i = 0; i < in_bits; ++i) {
2427 seed[i] = key.cws[i].s;
2428 tr |=
static_cast<In
>(key.cws[i].tr) << i;
2430 seed[in_bits] = key.ocw;
2431 EvalAll(b, key.s0, cuda::std::span<const int4>(seed, in_bits + 1), tr,
2432 cuda::std::span<const int4, 4>(key.cs), ys, cuda::std::span<int4, 4>(pi));
2435template <
int in_bits,
typename Group,
typename Prg,
typename XorHash,
typename Hash,
typename In>
2437 Groupable<Group> && Prgable<Prg, 2> && XorHashable<XorHash> && Hashable<Hash>)
2439 bool b,
const compat::detail::VdpfKeyRecord<Vdpf, Cw, in_bits> &key,
2440 cuda::std::span<int4> ys, cuda::std::array<int4, 4> &pi) {
2441 int4 seed[in_bits + 1];
2443 for (
int i = 0; i < in_bits; ++i) {
2444 seed[i] = key.cws[i].s;
2445 tr |=
static_cast<In
>(key.cws[i].tr) << i;
2447 seed[in_bits] = key.ocw;
2448 EvalAllMerkle(b, key.s0, cuda::std::span<const int4>(seed, in_bits + 1), tr,
2449 cuda::std::span<const int4, 4>(key.cs), ys, cuda::std::span<int4, 4>(pi));
2-party VDPF scheme.
Definition vdpf.cuh:115
cudaError_t EvalRangeMerkleGpu(bool b, __uint128_t first, __uint128_t last, int4 s0, cuda::std::span< const int4 > cws, __uint128_t tr, cuda::std::span< const int4, 4 > cs, cuda::std::span< int4 > ys, cuda::std::span< int4, 4 > pi, cudaStream_t stream=0, size_t stride=1) const
static bool Verify(cuda::std::span< const int4, 4 > pi0, cuda::std::span< const int4, 4 > pi1)
Verification method.
Definition vdpf.cuh:1592
__uint128_t Input
Definition vdpf.cuh:139
cuda::std::array< int4, 4 > Eval(bool b, int4 s0, cuda::std::span< const int4 > cws, In tr, cuda::std::span< const int4, 4 > cs, In x, int4 &y, size_t stride=1)
Evaluate one input from separate correction components.
Definition vdpf.cuh:1509
Vdpf(Prg prg, XorHash xor_hash, Hash hash)
Construct a scheme with configured backends.
Definition vdpf.cuh:1503
static constexpr int kCwCount
Definition vdpf.cuh:137
void ProveMerkle(cuda::std::span< const cuda::std::array< int4, 4 > > pi_tildes, cuda::std::span< const int4, 4 > cs, cuda::std::span< int4, 4 > pi, int par_depth=0)
Proof accumulation method (Merkle fold).
Definition vdpf.cuh:1709
void EvalAllMerkle(bool b, int4 s0, cuda::std::span< const int4 > cws, In tr, cuda::std::span< const int4, 4 > cs, cuda::std::span< int4 > ys, cuda::std::span< int4, 4 > pi, int par_depth=0, size_t stride=1)
Full-domain evaluation with the Merkle-fold proof from split correction words.
Definition vdpf.cuh:1771
void BatchEval(bool b, int4 s0, cuda::std::span< const int4 > cws, In tr, cuda::std::span< const int4, 4 > cs, cuda::std::span< const In > xs, cuda::std::span< int4 > ys, cuda::std::span< int4, 4 > pi, size_t stride=1)
Evaluate an arbitrary ordered query sequence and produce a chain proof from split correction words.
Definition vdpf.cuh:1876
cudaError_t EvalAllMerkleGpu(bool b, int4 s0, cuda::std::span< const int4 > cws, In tr, cuda::std::span< const int4, 4 > cs, cuda::std::span< int4 > ys, cuda::std::span< int4, 4 > pi, cudaStream_t stream=0, size_t stride=1) const
Launch full-domain CUDA evaluation with the canonical Merkle-fold proof from split device arrays.
Definition vdpf.cuh:2195
int Gen(cuda::std::span< int4 > cws, In &tr, cuda::std::span< int4, 4 > cs, 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 vdpf.cuh:1934
void BatchEvalMerkle(bool b, int4 s0, cuda::std::span< const int4 > cws, In tr, cuda::std::span< const int4, 4 > cs, cuda::std::span< const In > xs, cuda::std::span< int4 > ys, cuda::std::span< int4, 4 > pi, size_t stride=1)
Evaluate an arbitrary ordered query sequence and produce a Merkle proof from split correction words.
Definition vdpf.cuh:1917
void EvalAll(bool b, int4 s0, cuda::std::span< const int4 > cws, In tr, cuda::std::span< const int4, 4 > cs, cuda::std::span< int4 > ys, cuda::std::span< int4, 4 > pi, int par_depth=0, size_t stride=1)
Evaluate the full domain and produce a chain proof from split correction words.
Definition vdpf.cuh:1603
void EvalRange(bool b, In first, In last, int4 s0, cuda::std::span< const int4 > cws, In tr, cuda::std::span< const int4, 4 > cs, cuda::std::span< int4 > ys, cuda::std::span< int4, 4 > pi, int par_depth=0, size_t stride=1)
Evaluate the inclusive range [first, last] and produce a chain proof from split correction words.
Definition vdpf.cuh:1614
void EvalRangeMerkle(bool b, __uint128_t first, __uint128_t last, int4 s0, cuda::std::span< const int4 > cws, __uint128_t tr, cuda::std::span< const int4, 4 > cs, cuda::std::span< int4 > ys, cuda::std::span< int4, 4 > pi, int par_depth=0, size_t stride=1)
void Prove(cuda::std::span< const cuda::std::array< int4, 4 > > pi_tildes, cuda::std::span< const int4, 4 > cs, cuda::std::span< int4, 4 > pi)
Proof accumulation method.
Definition vdpf.cuh:1573
cudaError_t BatchEvalMerkleGpu(bool b, int4 s0, cuda::std::span< const int4 > cws, __uint128_t tr, cuda::std::span< const int4, 4 > cs, cuda::std::span< const __uint128_t > xs, cuda::std::span< int4 > ys, cuda::std::span< int4, 4 > pi, cudaStream_t stream=0, size_t stride=1) const
Single build-time switch for the deprecated v1 Cw-form compatibility entries.
Group interface.
Definition group.cuh:49
Optional four-message batch extension of Hashable.
Definition hash.cuh:50
Collision-resistant hash interface.
Definition hash.cuh:20
Pseudorandom generator (PRG) interface.
Definition prg.cuh:24
Collision-resistant and XOR-collision-resistant hash interface.
Definition hash.cuh:72
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
Correction word.
Definition vdpf.cuh:129
bool tr
Right control bit of the correction word. False and ignored in the final output word.
Definition vdpf.cuh:131
int4 s
Tree seed correction word, or the final output correction word at cws[in_bits].
Definition vdpf.cuh:130