66#include <cuda_runtime.h>
78#include <cuda/std/span>
95struct PackedHalfTreeCwsView {
99 __host__ __device__
inline Cw operator[](
int l)
const {
100 return Cw{cws[
static_cast<size_t>(l) * stride]};
106template <
int in_bits,
typename Hash,
typename In>
126template <
int in_bits,
int out_bits,
typename Hash,
typename In = u
int>
128 CcrHashable<Hash> && out_bits >= 1 && out_bits <= 64 && (out_bits & (out_bits - 1)) == 0)
134 static constexpr int kLanes = 128 / out_bits;
139 static constexpr int kDepth = in_bits - [] {
148 static_assert(kDepth >= 1,
"in_bits must exceed log2(kLanes) so that the tree has at least one level");
159 struct __align__(16)
Cw {
162 static_assert(
sizeof(
Cw) == 16);
205 __host__ __device__
void Gen(cuda::std::span<int4> cws,
206 cuda::std::span<const int4, 2> s0s, In a, uint64_t beta,
size_t stride = 1);
232 __host__ __device__ int4
Eval(
bool b, int4 s0, cuda::std::span<const int4> cws,
233 In x,
size_t stride = 1);
249 static __host__ __device__ uint64_t
Extract(int4 block, In x);
275 void EvalAll(
bool b, int4 s0, cuda::std::span<const int4> cws, int4 ys[],
276 int par_depth = 0,
size_t stride = 1);
306 void EvalRange(
bool b, In first, In last, int4 s0, cuda::std::span<const int4> cws,
307 cuda::std::span<int4> ys,
int par_depth = 0,
size_t stride = 1);
330 template <
int frontier_depth = 16,
int threads_per_block = 256>
331 __host__ cudaError_t
EvalAllGpu(
bool b, int4 s0, cuda::std::span<const int4> cws,
332 cuda::std::span<int4> ys, cudaStream_t stream = 0,
size_t stride = 1) const
333 requires(
kDepth < sizeof(
size_t) * 8);
363 template <
int frontier_depth = 16,
int threads_per_block = 256>
365 cuda::std::span<const int4> cws, cuda::std::span<int4> ys,
366 cudaStream_t stream = 0,
size_t stride = 0) const
367 requires(
kDepth < sizeof(
size_t) * 8);
369#if FSS_ENABLE_V1_COMPAT
380 using Key [[deprecated(
"use the split-component Gen/Eval/EvalAll with the packed-output terminal in the cws array")]] =
381 compat::detail::FcwKeyRecord<PackedHalfTreeDpf, Cw, kDepth>;
384 [[deprecated(
"beta must fit in out_bits bits; express the bound from out_bits instead")]]
385 static constexpr uint64_t kBetaMax = out_bits == 64 ? ~0ULL : (1ULL << out_bits) - 1;
388 [[deprecated(
"the constant described the removed scalar-top implementation and does not steer the current scheduling")]]
389 static constexpr int kTopDepth = 3;
392 [[deprecated(
"use the split-component Gen(cuda::std::span<int4>, cuda::std::span<const int4, 2>, In, uint64_t, size_t) overload")]]
393 __host__ __device__
void Gen(compat::detail::FcwKeyRecord<PackedHalfTreeDpf, Cw, kDepth> &k0,
394 compat::detail::FcwKeyRecord<PackedHalfTreeDpf, Cw, kDepth> &k1,
395 cuda::std::span<const int4, 2> s0s, In a, uint64_t beta);
398 [[deprecated(
"use the split-component Eval(bool, int4, cuda::std::span<const int4>, In, size_t) overload")]]
399 __host__ __device__ int4
Eval(
bool b,
const compat::detail::FcwKeyRecord<PackedHalfTreeDpf, Cw, kDepth> &key, In x);
402 [[deprecated(
"use the split-component EvalAll(bool, int4, cuda::std::span<const int4>, int4[], int, size_t) overload")]]
403 void EvalAll(
bool b,
const compat::detail::FcwKeyRecord<PackedHalfTreeDpf, Cw, kDepth> &key,
404 cuda::std::span<int4> ys);
408 friend class detail::SchemeAccess;
410 template <
int frontier_depth,
int threads_per_block>
411 struct GpuLaunchShape;
413 static __host__ __device__
void ExpandNode(Hash &hash, int4 instance_seed,
414 int4 node, int4 cw, int4 &left, int4 &right);
415 static __host__ __device__ int4 PathStep(Hash &hash, int4 instance_seed,
416 int4 node, int4 cw,
bool take_right);
417 static __host__ __device__ int4 ConvertLeaf(Hash &hash, int4 instance_seed,
418 int4 node, int4 output_cw);
425 using CwsView = detail::PackedHalfTreeCwsView<Cw>;
432 __host__ __device__
void Write(
int i, int4 s_cw) {
433 cws[
static_cast<size_t>(i) * stride] = s_cw;
436 __host__ __device__
void Finish(int4 terminal) {
437 cws[
static_cast<size_t>(kDepth) * stride] = terminal;
443 template <
typename Writer>
444 __host__ __device__
void Generate(Writer writer,
const int4 *s0s, In a, uint64_t beta);
447 template <
typename Cws>
448 __host__ __device__ int4 EvalCws(
bool b, int4 s0, Cws cws, In x);
451 template <
typename Cws>
452 void EvalAllCws(
bool b, int4 s0, Cws cws, int4 ys[],
int par_depth);
456 template <
typename Cws>
457 void EvalRangeCws(
bool b, In first, In last, int4 s0, Cws cws,
458 cuda::std::span<int4> ys,
int par_depth);
460 template <
typename Cws>
461 void EvalRangeTree(int4 node, Cws cws, int4 ys[], In first,
462 In last, In output_first, In low, In high,
int level);
464 template <
int other_bits,
typename OtherHash,
typename OtherIn>
471 static constexpr uint64_t kBetaLimit = out_bits == 64 ? ~0ULL : (1ULL << out_bits) - 1;
473 static constexpr int kBatchTopDepth = 3;
475 static __host__ __device__ int4 PackBeta(In a, uint64_t beta);
479 template <
typename Cws,
typename Visit>
480 __host__ __device__ int4 Walk(
bool b, int4 s0, Cws cws, In x, Visit &&visit);
484 static __host__ __device__ int4 MaskXor(int4 lhs, int4 rhs,
int mask);
487 __host__ __device__
void ExpandNode(int4 node, int4 cw, int4 &left, int4 &right);
493 template <
typename Cws>
494 void EvalAllFrontier(
bool b, int4 s0, Cws cws, int4 fcw, int4 ys[],
int par_depth);
498 void Expand8(
const int4 *src, int4 *dst,
size_t k, int4 cw);
504 void ExpandConvert8(
const int4 *src, int4 *ys,
size_t k, int4 cw, int4 fcw);
506 __host__ __device__ int4 ConvertLeaf(int4 node, int4 fcw);
511template <
int in_bits,
int out_bits,
typename Hash,
typename In>
513 CcrHashable<Hash> && out_bits >= 1 && out_bits <= 64 && (out_bits & (out_bits - 1)) == 0)
515 : hash_(hash), instance_seed_(instance_seed) {}
517template <
int in_bits,
int out_bits,
typename Hash,
typename In>
519 CcrHashable<Hash> && out_bits >= 1 && out_bits <= 64 && (out_bits & (out_bits - 1)) == 0)
520__host__ __device__
inline int4 PackedHalfTreeDpf<in_bits, out_bits, Hash, In>::PackBeta(
521 In a, uint64_t beta) {
522 int lane =
static_cast<int>(a) & (kLanes - 1);
523 int4 packed = {0, 0, 0, 0};
524 if constexpr (out_bits == 64) {
525 packed = lane == 0 ? int4{
static_cast<int>(beta),
static_cast<int>(beta >> 32), 0, 0}
526 : int4{0, 0,
static_cast<int>(beta),
static_cast<int>(beta >> 32)};
528 constexpr int lanes_per_word = 32 / out_bits;
529 int word = lane / lanes_per_word;
530 int off = (lane % lanes_per_word) * out_bits;
531 unsigned int v =
static_cast<unsigned int>(beta) << off;
532 if (word == 0) packed.x =
static_cast<int>(v);
533 else if (word == 1) packed.y =
static_cast<int>(v);
534 else if (word == 2) packed.z =
static_cast<int>(v);
535 else packed.w =
static_cast<int>(v);
540template <
int in_bits,
int out_bits,
typename Hash,
typename In>
542 CcrHashable<Hash> && out_bits >= 1 && out_bits <= 64 && (out_bits & (out_bits - 1)) == 0)
545 int lane =
static_cast<int>(x) & (
kLanes - 1);
546 if constexpr (out_bits == 64) {
547 uint64_t lo = lane == 0 ?
static_cast<unsigned int>(block.x) :
static_cast<unsigned int>(block.z);
548 uint64_t hi = lane == 0 ?
static_cast<unsigned int>(block.y) :
static_cast<unsigned int>(block.w);
549 return lo | (hi << 32);
551 constexpr int lanes_per_word = 32 / out_bits;
552 int word = lane / lanes_per_word;
553 int off = (lane % lanes_per_word) * out_bits;
554 unsigned int w = word == 0 ?
static_cast<unsigned int>(block.x)
555 : word == 1 ?
static_cast<unsigned int>(block.y)
556 : word == 2 ?
static_cast<unsigned int>(block.z)
557 :
static_cast<unsigned int>(block.w);
558 return (w >> off) &
static_cast<unsigned int>((1ULL << out_bits) - 1);
562template <
int in_bits,
int out_bits,
typename Hash,
typename In>
564 CcrHashable<Hash> && out_bits >= 1 && out_bits <= 64 && (out_bits & (out_bits - 1)) == 0)
566 cuda::std::span<int4> cws, cuda::std::span<const int4, 2> s0s, In a, uint64_t beta,
568 util::RequireArg(stride > 0,
"PackedHalfTreeDpf::Gen: stride must be positive");
569 constexpr size_t kMaxElements = SIZE_MAX /
sizeof(int4);
571 "PackedHalfTreeDpf::Gen: stride exceeds the correction array limit");
572 const size_t required =
static_cast<size_t>(
kDepth) * stride + 1;
574 "PackedHalfTreeDpf::Gen: cws must hold kDepth*stride+1 elements");
575 Generate(CwsWriter{cws.data(), stride}, s0s.data(), a, beta);
578template <
int in_bits,
int out_bits,
typename Hash,
typename In>
580 CcrHashable<Hash> && out_bits >= 1 && out_bits <= 64 && (out_bits & (out_bits - 1)) == 0)
581template <typename Writer>
582__host__ __device__
inline void PackedHalfTreeDpf<in_bits, out_bits, Hash, In>::Generate(
583 Writer writer,
const int4 *s0s, In a, uint64_t beta) {
584 util::RequireArg(beta <= kBetaLimit,
"PackedHalfTreeDpf::Gen: beta exceeds kBetaMax");
586 int4 packed_beta = PackBeta(a, beta);
594 for (
int i = 0; i < kDepth; ++i) {
595 int4 h0 = hash_.Hash(
util::Xor(instance_seed_, node0));
596 int4 h1 = hash_.Hash(
util::Xor(instance_seed_, node1));
598 bool a_bit = (a >> (in_bits - 1 - i)) & 1;
612 int4 zero4 = {0, 0, 0, 0};
613 int4 ab_mask0 = a_bit ? node0 : zero4;
614 int4 ab_mask1 = a_bit ? node1 : zero4;
615 int4 t0_mask = t0 ? cw : zero4;
616 int4 t1_mask = t1 ? cw : zero4;
626 int4 leaf0 = hash_.Hash(
util::Xor(instance_seed_, node0));
627 int4 leaf1 = hash_.Hash(
util::Xor(instance_seed_, node1));
632template <
int in_bits,
int out_bits,
typename Hash,
typename In>
634 CcrHashable<Hash> && out_bits >= 1 && out_bits <= 64 && (out_bits & (out_bits - 1)) == 0)
636 bool b, int4 s0, cuda::std::span<const int4> cws, In x,
size_t stride) {
637 util::RequireArg(stride > 0,
"PackedHalfTreeDpf::Eval: stride must be positive");
638 constexpr size_t kMaxElements = SIZE_MAX /
sizeof(int4);
640 "PackedHalfTreeDpf::Eval: stride exceeds the correction array limit");
641 const size_t required =
static_cast<size_t>(
kDepth) * stride + 1;
643 "PackedHalfTreeDpf::Eval: cws must hold kDepth*stride+1 elements");
644 return EvalCws(b, s0, CwsView{cws.data(), stride}, x);
647template <
int in_bits,
int out_bits,
typename Hash,
typename In>
649 CcrHashable<Hash> && out_bits >= 1 && out_bits <= 64 && (out_bits & (out_bits - 1)) == 0)
650template <typename Cws>
651__host__ __device__
inline int4 PackedHalfTreeDpf<in_bits, out_bits, Hash, In>::EvalCws(
652 bool b, int4 s0, Cws cws, In x) {
653 return ConvertLeaf(Walk(b, s0, cws, x, [](
bool,
bool) {}), cws[kDepth].s);
656template <
int in_bits,
int out_bits,
typename Hash,
typename In>
658 CcrHashable<Hash> && out_bits >= 1 && out_bits <= 64 && (out_bits & (out_bits - 1)) == 0)
659template <typename Cws, typename Visit>
660__host__ __device__
inline int4 PackedHalfTreeDpf<in_bits, out_bits, Hash, In>::Walk(
661 bool b, int4 s0, Cws cws, In x, Visit &&visit) {
664 for (
int i = 0; i < kDepth; ++i) {
665 bool x_bit = (x >> (in_bits - 1 - i)) & 1;
669 node = PathStep(hash_, instance_seed_, node, cws[i].s, x_bit);
675template <
int in_bits,
int out_bits,
typename Hash,
typename In>
677 CcrHashable<Hash> && out_bits >= 1 && out_bits <= 64 && (out_bits & (out_bits - 1)) == 0)
679 bool b, int4 s0, cuda::std::span<const int4> cws, int4 ys[],
int par_depth,
681 if (stride == 0)
throw std::invalid_argument(
"PackedHalfTreeDpf::EvalAll: stride must be positive");
682 constexpr size_t kMaxElements = SIZE_MAX /
sizeof(int4);
683 if (stride > (kMaxElements - 1) /
static_cast<size_t>(
kDepth))
684 throw std::invalid_argument(
"PackedHalfTreeDpf::EvalAll: stride exceeds the correction array limit");
685 const size_t required =
static_cast<size_t>(
kDepth) * stride + 1;
686 if (cws.size() < required)
687 throw std::invalid_argument(
"PackedHalfTreeDpf::EvalAll: cws must hold kDepth*stride+1 elements");
688 EvalAllCws(b, s0, CwsView{cws.data(), stride}, ys, par_depth);
691template <
int in_bits,
int out_bits,
typename Hash,
typename In>
693 CcrHashable<Hash> && out_bits >= 1 && out_bits <= 64 && (out_bits & (out_bits - 1)) == 0)
694template <typename Cws>
695inline void PackedHalfTreeDpf<in_bits, out_bits, Hash, In>::EvalAllCws(
696 bool b, int4 s0, Cws cws, int4 ys[],
int par_depth) {
697 assert(par_depth >= 0);
698 static_assert(detail::FitsFullDomain<int4, kDepth>(),
699 "packed output count and byte size must fit in size_t");
700 int4 fcw = cws[kDepth].s;
701 if constexpr (kDepth > kBatchTopDepth) {
702 EvalAllFrontier(b, s0, cws, fcw, ys, par_depth);
706 constexpr size_t blocks =
size_t{1} << kDepth;
707 fss::detail::ParallelFor(par_depth, blocks, [&](
size_t j) {
708 ys[j] = EvalCws(b, s0, cws,
static_cast<In
>(j * kLanes));
713template <
int in_bits,
int out_bits,
typename Hash,
typename In>
715 CcrHashable<Hash> && out_bits >= 1 && out_bits <= 64 && (out_bits & (out_bits - 1)) == 0)
717 In last, int4 s0, cuda::std::span<const int4> cws, cuda::std::span<int4> ys,
718 int par_depth,
size_t stride) {
720 throw std::invalid_argument(
"PackedHalfTreeDpf::EvalRange: stride must be positive");
721 constexpr size_t kMaxElements = SIZE_MAX /
sizeof(int4);
722 if (stride > (kMaxElements - 1) /
static_cast<size_t>(
kDepth))
723 throw std::invalid_argument(
"PackedHalfTreeDpf::EvalRange: stride exceeds the correction array limit");
724 const size_t required =
static_cast<size_t>(
kDepth) * stride + 1;
725 if (cws.size() < required || cws.data() ==
nullptr)
726 throw std::invalid_argument(
"PackedHalfTreeDpf::EvalRange: cws must hold kDepth*stride+1 elements");
727 if (ys.data() ==
nullptr)
728 throw std::invalid_argument(
"PackedHalfTreeDpf::EvalRange: ys must not be null");
729 EvalRangeCws(b, first, last, s0, CwsView{cws.data(), stride}, ys, par_depth);
732template <
int in_bits,
int out_bits,
typename Hash,
typename In>
734 CcrHashable<Hash> && out_bits >= 1 && out_bits <= 64 && (out_bits & (out_bits - 1)) == 0)
735template <typename Cws>
736inline void PackedHalfTreeDpf<in_bits, out_bits, Hash, In>::EvalRangeCws(
737 bool b, In first, In last, int4 s0, Cws cws,
738 cuda::std::span<int4> ys,
int par_depth) {
739 assert(par_depth >= 0);
740 In domain_last =
static_cast<In
>(~In{0});
741 if constexpr (in_bits <
sizeof(In) * 8) {
742 domain_last =
static_cast<In
>((In{1} << in_bits) - 1);
745 throw std::invalid_argument(
"PackedHalfTreeDpf::EvalRange: first must not exceed last");
746 if (last > domain_last)
747 throw std::invalid_argument(
"PackedHalfTreeDpf::EvalRange: last must stay inside the input domain");
748 const __uint128_t distance =
static_cast<__uint128_t
>(last) - first;
749 const __uint128_t block_distance = distance / kLanes;
753 if (block_distance >= std::numeric_limits<size_t>::max() /
sizeof(int4))
754 throw std::invalid_argument(
"PackedHalfTreeDpf::EvalRange: the interval is too large for size_t");
755 const size_t blocks =
static_cast<size_t>(block_distance) + 1;
756 if (ys.size() < blocks || ys.data() ==
nullptr)
757 throw std::invalid_argument(
"PackedHalfTreeDpf::EvalRange: ys must hold the covered blocks");
759 const size_t partitions = detail::ResolveParPartitions(par_depth, blocks);
760 detail::ParallelFor(par_depth, partitions, [&](
size_t partition) {
761 const size_t begin = partition * (blocks / partitions) +
762 (partition < blocks % partitions ? partition : blocks % partitions);
763 const size_t end = begin + blocks / partitions + (partition < blocks % partitions);
764 for (
size_t index = begin; index < end; ++index) ys[index] = {0, 0, 0, 0};
765 const __uint128_t begin_offset =
static_cast<__uint128_t
>(begin) * kLanes;
766 const __uint128_t end_offset = end == blocks ? distance :
767 static_cast<__uint128_t
>(end) * kLanes - 1;
769 static_cast<In
>(first + begin_offset),
static_cast<In
>(first + end_offset),
770 first, In{0}, domain_last, 0);
774template <
int in_bits,
int out_bits,
typename Hash,
typename In>
776 CcrHashable<Hash> && out_bits >= 1 && out_bits <= 64 && (out_bits & (out_bits - 1)) == 0)
777template <typename Cws>
778inline void PackedHalfTreeDpf<in_bits, out_bits, Hash, In>::EvalRangeTree(
779 int4 node, Cws cws, int4 ys[], In first, In last,
780 In output_first, In low, In high,
int level) {
781 if (level == kDepth) {
782 const int4 block = ConvertLeaf(node, cws[kDepth].s);
783 const In begin = first > low ? first : low;
784 const In end = last < high ? last : high;
785 const size_t lanes =
static_cast<size_t>(end - begin) + 1;
786 for (
size_t lane = 0; lane < lanes; ++lane) {
787 const In x =
static_cast<In
>(begin + lane);
788 const In offset =
static_cast<In
>(x - output_first);
789 const size_t index =
static_cast<size_t>(offset / kLanes);
790 ys[index] =
util::Xor(ys[index], PackBeta(offset, Extract(block, x)));
794 const In middle =
static_cast<In
>(low + ((high - low) >> 1));
796 ExpandNode(node, cws[level].s, left, right);
797 if (first <= middle) {
798 EvalRangeTree(left, cws, ys, first, last, output_first, low, middle, level + 1);
801 EvalRangeTree(right, cws, ys, first, last, output_first,
802 static_cast<In
>(middle + 1), high, level + 1);
806template <
int in_bits,
int out_bits,
typename Hash,
typename In>
808 CcrHashable<Hash> && out_bits >= 1 && out_bits <= 64 && (out_bits & (out_bits - 1)) == 0)
809__host__ __device__
inline int4 PackedHalfTreeDpf<in_bits, out_bits, Hash, In>::MaskXor(
810 int4 lhs, int4 rhs,
int mask) {
811 return {lhs.x ^ (rhs.x & mask), lhs.y ^ (rhs.y & mask), lhs.z ^ (rhs.z & mask), lhs.w ^ (rhs.w & mask)};
814template <
int in_bits,
int out_bits,
typename Hash,
typename In>
816 CcrHashable<Hash> && out_bits >= 1 && out_bits <= 64 && (out_bits & (out_bits - 1)) == 0)
817__host__ __device__
inline void PackedHalfTreeDpf<in_bits, out_bits, Hash, In>::ExpandNode(
818 int4 node, int4 cw, int4 &left, int4 &right) {
819 ExpandNode(hash_, instance_seed_, node, cw, left, right);
822template <
int in_bits,
int out_bits,
typename Hash,
typename In>
824 CcrHashable<Hash> && out_bits >= 1 && out_bits <= 64 && (out_bits & (out_bits - 1)) == 0)
825template <typename Cws>
826inline void PackedHalfTreeDpf<in_bits, out_bits, Hash, In>::EvalAllFrontier(
827 bool b, int4 s0, Cws cws, int4 fcw, int4 ys[],
int par_depth) {
828 constexpr size_t num_blocks = detail::FullDomainSize<int4, kDepth>();
834 static thread_local std::vector<int4> scratch;
835 if (scratch.size() < num_blocks) scratch.resize(num_blocks);
836 int4 *bufs[2] = {scratch.data(), scratch.data() + num_blocks / 2};
840 for (
int i = 0; i < kBatchTopDepth; ++i) {
841 size_t count =
size_t{1} << i;
842 for (
size_t j = 0; j < count; ++j) {
843 ExpandNode(bufs[i & 1][j], cws[i].s, bufs[(i + 1) & 1][2 * j], bufs[(i + 1) & 1][2 * j + 1]);
849 int depth = fss::detail::ResolveParDepth(par_depth, kDepth);
851 for (
int i = kBatchTopDepth; i < kDepth; ++i) {
852 const int4 *src = bufs[i & 1];
853 int4 *dst = i + 1 == kDepth ? ys : bufs[(i + 1) & 1];
854 size_t batches = (
size_t{1} << i) / 8;
855 for (
size_t k = 0; k < batches; ++k) {
856 if (i + 1 == kDepth) ExpandConvert8(src, dst, k, cws[i].s, fcw);
857 else Expand8(src, dst, k, cws[i].s);
862 int threads = fss::detail::ResolveParThreads(depth, num_blocks / 16);
863#pragma omp parallel num_threads(threads)
865 for (
int i = kBatchTopDepth; i < kDepth; ++i) {
866 const int4 *src = bufs[i & 1];
867 int4 *dst = i + 1 == kDepth ? ys : bufs[(i + 1) & 1];
868 size_t batches = (
size_t{1} << i) / 8;
869 size_t partitions = fss::detail::ResolveParPartitions(depth, batches);
870#pragma omp for schedule(static)
871 for (
size_t partition = 0; partition < partitions; ++partition) {
872 size_t begin = partition * (batches / partitions) +
873 (partition < batches % partitions ? partition : batches % partitions);
874 size_t end = begin + batches / partitions + (partition < batches % partitions);
875 for (
size_t k = begin; k < end; ++k) {
876 if (i + 1 == kDepth) ExpandConvert8(src, dst, k, cws[i].s, fcw);
877 else Expand8(src, dst, k, cws[i].s);
884template <
int in_bits,
int out_bits,
typename Hash,
typename In>
886 CcrHashable<Hash> && out_bits >= 1 && out_bits <= 64 && (out_bits & (out_bits - 1)) == 0)
887inline void PackedHalfTreeDpf<in_bits, out_bits, Hash, In>::Expand8(
888 const int4 *src, int4 *dst,
size_t k, int4 cw) {
890 cuda::std::array<int4, 8> seeds;
891 for (
int j = 0; j < 8; ++j) {
892 parents[j] = src[8 * k + j];
893 seeds[j] =
util::Xor(instance_seed_, parents[j]);
895 auto hs = detail::HashBatch(hash_, seeds);
896 for (
int j = 0; j < 8; ++j) {
897 int4 left = MaskXor(hs[j], cw, -
static_cast<int>(
util::GetLsb(parents[j])));
898 dst[16 * k + 2 * j] = left;
899 dst[16 * k + 2 * j + 1] =
util::Xor(left, parents[j]);
903template <
int in_bits,
int out_bits,
typename Hash,
typename In>
905 CcrHashable<Hash> && out_bits >= 1 && out_bits <= 64 && (out_bits & (out_bits - 1)) == 0)
906inline void PackedHalfTreeDpf<in_bits, out_bits, Hash, In>::ExpandConvert8(
907 const int4 *src, int4 *ys,
size_t k, int4 cw, int4 fcw) {
909 cuda::std::array<int4, 8> seeds;
910 for (
int j = 0; j < 8; ++j) {
911 parents[j] = src[8 * k + j];
912 seeds[j] =
util::Xor(instance_seed_, parents[j]);
914 auto hs = detail::HashBatch(hash_, seeds);
917 cuda::std::array<int4, 8> conv_seeds[2];
918 for (
int j = 0; j < 8; ++j) {
919 int4 left = MaskXor(hs[j], cw, -
static_cast<int>(
util::GetLsb(parents[j])));
920 children[2 * j] = left;
921 children[2 * j + 1] =
util::Xor(left, parents[j]);
922 conv_seeds[0][j] =
util::Xor(instance_seed_, children[2 * j]);
923 conv_seeds[1][j] =
util::Xor(instance_seed_, children[2 * j + 1]);
925 auto leaves_even = detail::HashBatch(hash_, conv_seeds[0]);
926 auto leaves_odd = detail::HashBatch(hash_, conv_seeds[1]);
927 for (
int j = 0; j < 8; ++j) {
928 ys[16 * k + 2 * j] = MaskXor(leaves_even[j], fcw, -
static_cast<int>(
util::GetLsb(children[2 * j])));
929 ys[16 * k + 2 * j + 1] =
930 MaskXor(leaves_odd[j], fcw, -
static_cast<int>(
util::GetLsb(children[2 * j + 1])));
934template <
int in_bits,
int out_bits,
typename Hash,
typename In>
936 CcrHashable<Hash> && out_bits >= 1 && out_bits <= 64 && (out_bits & (out_bits - 1)) == 0)
937__host__ __device__
inline int4 PackedHalfTreeDpf<in_bits, out_bits, Hash, In>::ConvertLeaf(
938 int4 node, int4 fcw) {
939 return ConvertLeaf(hash_, instance_seed_, node, fcw);
942template <
int in_bits,
int out_bits,
typename Hash,
typename In>
944 CcrHashable<Hash> && out_bits >= 1 && out_bits <= 64 && (out_bits & (out_bits - 1)) == 0)
945__host__ __device__
inline void PackedHalfTreeDpf<in_bits, out_bits, Hash, In>::ExpandNode(
946 Hash &hash, int4 instance_seed, int4 node, int4 cw, int4 &left, int4 &right) {
947 int4 hashed = hash.Hash(
util::Xor(instance_seed, node));
948 left = MaskXor(hashed, cw, -
static_cast<int>(
util::GetLsb(node)));
952template <
int in_bits,
int out_bits,
typename Hash,
typename In>
954 CcrHashable<Hash> && out_bits >= 1 && out_bits <= 64 && (out_bits & (out_bits - 1)) == 0)
955__host__ __device__
inline int4 PackedHalfTreeDpf<in_bits, out_bits, Hash, In>::PathStep(
956 Hash &hash, int4 instance_seed, int4 node, int4 cw,
bool take_right) {
957 const int4 zero = {0, 0, 0, 0};
958 int4 hashed = hash.Hash(
util::Xor(instance_seed, node));
963template <
int in_bits,
int out_bits,
typename Hash,
typename In>
965 CcrHashable<Hash> && out_bits >= 1 && out_bits <= 64 && (out_bits & (out_bits - 1)) == 0)
966__host__ __device__
inline int4 PackedHalfTreeDpf<in_bits, out_bits, Hash, In>::ConvertLeaf(
967 Hash &hash, int4 instance_seed, int4 node, int4 output_cw) {
968 int4 leaf = hash.Hash(
util::Xor(instance_seed, node));
973namespace gpu::detail {
975template <
int in_bits,
int out_bits,
typename Hash,
typename In>
976 requires(ValidForInputBits<In, in_bits> &&
977 CcrHashable<Hash> && out_bits >= 1 && out_bits <= 64 && (out_bits & (out_bits - 1)) == 0)
978struct HalfTreeGpuTraits<PackedHalfTreeDpf<in_bits, out_bits, Hash, In>> {
979 using Backend = Hash;
982 static constexpr bool kPacked =
true;
985#if defined(__CUDACC__)
990template <
typename Scheme,
int frontier_depth,
int block_root_depth>
991__global__
void PackedHalfTreeSplitEvalAllKernel(
bool b, int4 s0,
const void *cws_buffer,
992 size_t stride, int4 *ys, int4 instance_seed,
typename HalfTreeGpuTraits<Scheme>::Backend hash) {
993 using Cws = fss::detail::PackedHalfTreeCwsView<typename Scheme::Cw>;
994 const auto *cws =
static_cast<const int4 *
>(cws_buffer);
995 const int64_t thread_index =
static_cast<int64_t
>(blockIdx.x) * blockDim.x + threadIdx.x;
996 if (thread_index >= (1LL << frontier_depth))
return;
997 __shared__ int4 shared_frontier[1 << (frontier_depth - block_root_depth)];
998 HalfTreeEvalAllBlock<Scheme, frontier_depth, block_root_depth>(b, s0,
999 Cws{cws, stride}, ys, instance_seed, hash, blockIdx.x, thread_index,
1003template <
typename Scheme,
int frontier_depth,
int block_root_depth>
1004__global__
void PackedHalfTreeSplitEvalAllBatchKernel(
bool b,
const int4 *s0s,
1005 const void *cws_buffer,
size_t stride, int4 *ys, int4 instance_seed,
1006 typename HalfTreeGpuTraits<Scheme>::Backend hash) {
1007 using Cws = fss::detail::PackedHalfTreeCwsView<typename Scheme::Cw>;
1008 const auto *cws =
static_cast<const int4 *
>(cws_buffer);
1009 constexpr int kBlocksPerKey = 1 << block_root_depth;
1010 constexpr size_t kOutputsPerKey =
size_t{1} << HalfTreeGpuTraits<Scheme>::kOutputBits;
1011 const int key_index = blockIdx.x / kBlocksPerKey;
1012 const int subtree_block_index = blockIdx.x - key_index * kBlocksPerKey;
1013 const int64_t thread_index =
static_cast<int64_t
>(subtree_block_index) * blockDim.x + threadIdx.x;
1014 if (thread_index >= (1LL << frontier_depth))
return;
1015 int4 *key_outputs = ys +
static_cast<size_t>(key_index) * kOutputsPerKey;
1016 __shared__ int4 shared_frontier[1 << (frontier_depth - block_root_depth)];
1017 HalfTreeEvalAllBlock<Scheme, frontier_depth, block_root_depth>(b, s0s[key_index],
1018 Cws{cws + key_index, stride}, key_outputs, instance_seed, hash,
1019 subtree_block_index, thread_index, shared_frontier);
1024template <
typename Scheme,
int frontier_depth,
int block_root_depth>
1025__global__
void PackedHalfTreeSplitEvalAllBatchDeviceKeyKernel(
bool b,
const int4 *s0s,
1026 const void *cws_buffer,
size_t key_records,
size_t key_count, int4 *ys, int4 instance_seed,
1027 typename HalfTreeGpuTraits<Scheme>::Backend hash) {
1028 using Cws = fss::detail::PackedHalfTreeCwsView<typename Scheme::Cw>;
1029 const auto *cws =
static_cast<const int4 *
>(cws_buffer);
1030 constexpr int kBlocksPerKey = 1 << block_root_depth;
1031 constexpr size_t kOutputsPerKey =
size_t{1} << HalfTreeGpuTraits<Scheme>::kOutputBits;
1032 const int key_index = blockIdx.x / kBlocksPerKey;
1033 const int subtree_block_index = blockIdx.x - key_index * kBlocksPerKey;
1034 const int64_t thread_index =
static_cast<int64_t
>(subtree_block_index) * blockDim.x + threadIdx.x;
1035 if (thread_index >= (1LL << frontier_depth))
return;
1036 const int4 *key = cws +
static_cast<size_t>(key_index) * key_records;
1037 int4 *key_outputs = ys +
static_cast<size_t>(key_index) * kOutputsPerKey;
1038 __shared__ int4 shared_frontier[1 << (frontier_depth - block_root_depth)];
1039 HalfTreeEvalAllBlock<Scheme, frontier_depth, block_root_depth>(b, s0s[key_index],
1040 Cws{key, 1}, key_outputs, instance_seed, hash,
1041 subtree_block_index, thread_index, shared_frontier);
1046template <
int in_bits,
int out_bits,
typename Hash,
typename In,
int frontier_depth = 16,
1047 int threads_per_block = 256>
1048__host__
inline cudaError_t PackedHalfTreeDpfEvalAllGpuBatchDeviceKey(
bool b,
const int4 *s0s,
1049 size_t key_count,
const int4 *cws,
size_t cws_count,
size_t key_records, int4 *ys,
1050 size_t ys_count, int4 instance_seed,
const Hash &hash, cudaStream_t stream) {
1051 using Scheme = PackedHalfTreeDpf<in_bits, out_bits, Hash, In>;
1052 constexpr int kDepth = Scheme::kDepth;
1053 constexpr size_t kOutputsPerKey =
size_t{1} << kDepth;
1054 constexpr size_t kMaxElements = std::numeric_limits<size_t>::max() /
sizeof(int4);
1055 constexpr size_t kMaxAddressableSize = std::numeric_limits<size_t>::max();
1056 constexpr int kFrontierDepth = frontier_depth < kDepth ? frontier_depth : kDepth;
1057 static_assert(threads_per_block >= 1 && threads_per_block <= 1024 &&
1058 (threads_per_block & (threads_per_block - 1)) == 0,
1059 "gpu block size must be a power of two in [1,1024]");
1060 static_assert(kDepth - kFrontierDepth <= 8,
1061 "gpu frontier leaves more than eight tail levels; specify a larger frontier_depth");
1062 constexpr int kThreadsPerBlockLog2 = [] {
1063 int requested_log2 = 0;
1064 for (
int threads = threads_per_block; threads > 1; threads >>= 1) ++requested_log2;
1065 return requested_log2 < kFrontierDepth ? requested_log2 : kFrontierDepth;
1067 constexpr int kBlockRootDepth = kFrontierDepth - kThreadsPerBlockLog2;
1068 constexpr int kBlocksPerKey = 1 << kBlockRootDepth;
1069 if (key_count == 0)
return cudaSuccess;
1070 if (key_count > kMaxElements ||
1071 key_count > kMaxAddressableSize / kOutputsPerKey /
sizeof(int4) ||
1072 key_count > kMaxAddressableSize / key_records /
sizeof(int4) ||
1073 cws_count < key_count * key_records || ys_count < key_count * kOutputsPerKey ||
1074 s0s ==
nullptr || cws ==
nullptr || ys ==
nullptr)
return cudaErrorInvalidValue;
1075 if (key_count >
static_cast<size_t>(std::numeric_limits<int>::max()) / kBlocksPerKey)
1076 return cudaErrorInvalidConfiguration;
1077 PackedHalfTreeSplitEvalAllBatchDeviceKeyKernel<Scheme, kFrontierDepth, kBlockRootDepth>
1078 <<<key_count * kBlocksPerKey, 1 << kThreadsPerBlockLog2, 0, stream>>>(
1079 b, s0s, cws, key_records, key_count, ys, instance_seed, hash);
1080 return cudaGetLastError();
1085#if defined(__CUDACC__)
1092template <
int in_bits,
int unroll,
bool b,
bool cmp,
typename Scheme,
typename In,
typename Query,
typename Y>
1093__global__
void PackedHalfTreeDpfEvalPointKernel(Y *ys,
const int4 *seeds,
const int4 *cws,
const Query *xs,
1094 int nkeys,
typename HalfTreeGpuTraits<Scheme>::Backend hash, int4 instance_seed) {
1095 int tid = blockIdx.x * blockDim.x + threadIdx.x;
1096 if (tid >= nkeys)
return;
1098 In x =
static_cast<In
>(xs[tid]);
1099 fss::detail::GrottoTurns turns;
1101#pragma unroll unroll
1102 for (
int i = 0; i < Scheme::kDepth; ++i) {
1103 bool x_bit = GetBit(x, in_bits - 1 - i);
1105 if constexpr (cmp) turns(t, x_bit);
1107 int4 cw = cws[(size_t)i * nkeys + tid];
1113 if constexpr (cmp) {
1114 ys[tid] = turns.share ^ fss::detail::SchemeAccess::HalfTreeGrottoLeafParity<Scheme>(leaf, x);
1120template <
int unroll,
bool cmp,
int in_bits,
typename Scheme,
typename In,
typename Query,
typename Y>
1121void PackedHalfTreeDpfEvalPointLaunch(
bool b,
const int4 *seeds,
const int4 *cws,
const Query *xs, Y *ys,
1122 int nkeys,
typename HalfTreeGpuTraits<Scheme>::Backend hash, int4 instance_seed, cudaStream_t stream) {
1123 if (nkeys == 0)
return;
1124 constexpr int kBs = 256;
1125 int blocks = (nkeys + kBs - 1) / kBs;
1127 PackedHalfTreeDpfEvalPointKernel<in_bits, unroll, true, cmp, Scheme, In, Query, Y>
1128 <<<blocks, kBs, 0, stream>>>(ys, seeds, cws, xs, nkeys, hash, instance_seed);
1130 PackedHalfTreeDpfEvalPointKernel<in_bits, unroll, false, cmp, Scheme, In, Query, Y>
1131 <<<blocks, kBs, 0, stream>>>(ys, seeds, cws, xs, nkeys, hash, instance_seed);
1151template <
int unroll = 4,
int in_bits,
int out_bits,
typename Hash,
typename In,
typename Query = In>
1152 requires QueryFor<Query, in_bits>
1153void PackedHalfTreeDpfEvalPointGpu(
bool b, cuda::std::span<const int4> seeds, cuda::std::span<const int4> cws,
1154 cuda::std::span<const Query> xs, int4 *ys,
int nkeys,
const PackedHalfTreeDpf<in_bits, out_bits, Hash, In> &dpf,
1155 cudaStream_t stream = 0) {
1157 throw std::invalid_argument(
"PackedHalfTreeDpfEvalPointGpu: nkeys must be nonnegative");
1158 if (nkeys == 0)
return;
1159 const size_t count =
static_cast<size_t>(nkeys);
1161 if (count > SIZE_MAX / kLevels || cws.size() < count * kLevels)
1162 throw std::invalid_argument(
"PackedHalfTreeDpfEvalPointGpu: cws must hold (kDepth+1)*nkeys elements");
1163 if (seeds.size() < count || xs.size() < count || ys ==
nullptr)
1164 throw std::invalid_argument(
"PackedHalfTreeDpfEvalPointGpu: seeds, xs, and ys must cover nkeys entries");
1165 using Scheme = PackedHalfTreeDpf<in_bits, out_bits, Hash, In>;
1166 PackedHalfTreeDpfEvalPointLaunch<unroll, false, in_bits, Scheme, In, Query, int4>(b, seeds.data(), cws.data(),
1167 xs.data(), ys, nkeys, fss::detail::SchemeAccess::GetHash(dpf),
1168 fss::detail::SchemeAccess::GetInstanceSeed(dpf), stream);
1175template <
int in_bits,
int out_bits,
typename Hash,
typename In>
1177 CcrHashable<Hash> && out_bits >= 1 && out_bits <= 64 && (out_bits & (out_bits - 1)) == 0)
1178template <int frontier_depth, int threads_per_block>
1180struct PackedHalfTreeDpf<in_bits, out_bits, Hash, In>::GpuLaunchShape {
1181 static_assert(frontier_depth >= 0,
"gpu frontier depth must be nonnegative");
1182 static_assert(1 <= threads_per_block && threads_per_block <= 1024 &&
1183 (threads_per_block & (threads_per_block - 1)) == 0,
1184 "gpu block size must be a power of two in [1,1024]");
1185 static_assert(kDepth >= 0,
"gpu tree must have a nonnegative expansion depth");
1186 static constexpr int kFrontierDepth = frontier_depth < kDepth ? frontier_depth : kDepth;
1187 static_assert(kDepth - kFrontierDepth <= 8,
1188 "gpu frontier leaves more than eight tail levels; specify a larger frontier_depth");
1189 static constexpr int kThreadsPerBlockLog2 = [] {
1191 for (
int count = threads_per_block; count > 1; count >>= 1) ++depth;
1192 return depth < kFrontierDepth ? depth : kFrontierDepth;
1194 static constexpr int kThreadsPerBlock = 1 << kThreadsPerBlockLog2;
1195 static constexpr int kBlockRootDepth = kFrontierDepth - kThreadsPerBlockLog2;
1196 static_assert(kBlockRootDepth <= 30,
"gpu block index exceeds the signed-int range");
1197 static constexpr size_t kBlocksPerKey =
size_t{1} << (kBlockRootDepth <= 30 ? kBlockRootDepth : 0);
1200template <
int in_bits,
int out_bits,
typename Hash,
typename In>
1202 CcrHashable<Hash> && out_bits >= 1 && out_bits <= 64 && (out_bits & (out_bits - 1)) == 0)
1203template <int frontier_depth, int threads_per_block>
1205 bool b, int4 s0, cuda::std::span<const int4> cws, cuda::std::span<int4> ys,
1206 cudaStream_t stream,
size_t stride)
const
1207 requires(
kDepth <
sizeof(size_t) * 8) {
1208#if defined(__CUDACC__)
1209 using LaunchShape = GpuLaunchShape<frontier_depth, threads_per_block>;
1211 constexpr size_t kOutputsPerKey =
size_t{1} <<
kDepth;
1212 constexpr size_t kMaxElements = std::numeric_limits<size_t>::max() /
sizeof(int4);
1213 if (stride == 0 || stride > (kMaxElements - 1) /
static_cast<size_t>(
kDepth))
1214 return cudaErrorInvalidValue;
1215 const size_t required =
static_cast<size_t>(
kDepth) * stride + 1;
1216 if (cws.size() < required || ys.size() < kOutputsPerKey ||
1217 cws.data() ==
nullptr || ys.data() ==
nullptr ||
1218 kOutputsPerKey > std::numeric_limits<size_t>::max() /
sizeof(int4))
1219 return cudaErrorInvalidValue;
1220 gpu::detail::PackedHalfTreeSplitEvalAllKernel<Scheme, LaunchShape::kFrontierDepth,
1221 LaunchShape::kBlockRootDepth>
1222 <<<LaunchShape::kBlocksPerKey, LaunchShape::kThreadsPerBlock, 0, stream>>>(
1223 b, s0, cws.data(), stride, ys.data(), instance_seed_, hash_);
1224 return cudaGetLastError();
1226 static_assert(
sizeof(Hash) == 0,
"gpu launch requires a CUDA compiler");
1227 return cudaErrorNotSupported;
1231template <
int in_bits,
int out_bits,
typename Hash,
typename In>
1233 CcrHashable<Hash> && out_bits >= 1 && out_bits <= 64 && (out_bits & (out_bits - 1)) == 0)
1234template <int frontier_depth, int threads_per_block>
1236 bool b, cuda::std::span<const int4> s0s, cuda::std::span<const int4> cws,
1237 cuda::std::span<int4> ys, cudaStream_t stream,
size_t stride)
const
1238 requires(
kDepth <
sizeof(size_t) * 8) {
1239#if defined(__CUDACC__)
1240 using LaunchShape = GpuLaunchShape<frontier_depth, threads_per_block>;
1242 constexpr size_t kOutputsPerKey =
size_t{1} <<
kDepth;
1243 constexpr size_t kMaxElements = std::numeric_limits<size_t>::max() /
sizeof(int4);
1244 constexpr size_t kMaxAddressableSize = std::numeric_limits<size_t>::max();
1245 const size_t key_count = s0s.size();
1246 if (key_count == 0)
return cudaSuccess;
1249 if (key_count > kMaxElements ||
1250 key_count > kMaxAddressableSize / kOutputsPerKey /
sizeof(int4) ||
1251 (stride != 0 && (stride < key_count || stride > kMaxElements)))
1252 return cudaErrorInvalidValue;
1254 const size_t resolved_stride = stride == 0 ? key_count : stride;
1255 if (resolved_stride > (kMaxElements - key_count) /
static_cast<size_t>(
kDepth))
1256 return cudaErrorInvalidValue;
1257 const size_t required =
static_cast<size_t>(
kDepth) * resolved_stride + key_count;
1258 if (cws.size() < required || ys.size() < key_count * kOutputsPerKey ||
1259 s0s.data() ==
nullptr || cws.data() ==
nullptr || ys.data() ==
nullptr)
1260 return cudaErrorInvalidValue;
1261 if (key_count >
static_cast<size_t>(std::numeric_limits<int>::max()) / LaunchShape::kBlocksPerKey)
1262 return cudaErrorInvalidConfiguration;
1263 gpu::detail::PackedHalfTreeSplitEvalAllBatchKernel<Scheme, LaunchShape::kFrontierDepth,
1264 LaunchShape::kBlockRootDepth>
1265 <<<key_count * LaunchShape::kBlocksPerKey, LaunchShape::kThreadsPerBlock, 0, stream>>>(
1266 b, s0s.data(), cws.data(), resolved_stride, ys.data(), instance_seed_, hash_);
1267 return cudaGetLastError();
1269 static_assert(
sizeof(Hash) == 0,
"gpu launch requires a CUDA compiler");
1270 return cudaErrorNotSupported;
1274#if FSS_ENABLE_V1_COMPAT
1280template <
int in_bits,
int out_bits,
typename Hash,
typename In>
1282 CcrHashable<Hash> && out_bits >= 1 && out_bits <= 64 && (out_bits & (out_bits - 1)) == 0)
1284 compat::detail::FcwKeyRecord<PackedHalfTreeDpf, Cw, kDepth> &k0,
1285 compat::detail::FcwKeyRecord<PackedHalfTreeDpf, Cw, kDepth> &k1,
1286 cuda::std::span<const int4, 2> s0s, In a, uint64_t beta) {
1287 int4 seed[kDepth + 1];
1288 Gen(cuda::std::span<int4>(seed, kDepth + 1), s0s, a, beta);
1289 for (
int i = 0; i < kDepth; ++i) {
1290 const Cw layer = {seed[i]};
1294 k0.fcw = seed[kDepth];
1295 k1.fcw = seed[kDepth];
1300template <
int in_bits,
int out_bits,
typename Hash,
typename In>
1302 CcrHashable<Hash> && out_bits >= 1 && out_bits <= 64 && (out_bits & (out_bits - 1)) == 0)
1304 bool b,
const compat::detail::FcwKeyRecord<PackedHalfTreeDpf, Cw, kDepth> &key, In x) {
1305 int4 seed[kDepth + 1];
1306 for (
int i = 0; i < kDepth; ++i) seed[i] = key.cws[i].s;
1307 seed[kDepth] = key.fcw;
1308 return Eval(b, key.s0, cuda::std::span<const int4>(seed, kDepth + 1), x);
1311template <
int in_bits,
int out_bits,
typename Hash,
typename In>
1313 CcrHashable<Hash> && out_bits >= 1 && out_bits <= 64 && (out_bits & (out_bits - 1)) == 0)
1315 bool b,
const compat::detail::FcwKeyRecord<PackedHalfTreeDpf, Cw, kDepth> &key,
1316 cuda::std::span<int4> ys) {
1317 int4 seed[kDepth + 1];
1318 for (
int i = 0; i < kDepth; ++i) seed[i] = key.cws[i].s;
1319 seed[kDepth] = key.fcw;
1320 EvalAll(b, key.s0, cuda::std::span<const int4>(seed, kDepth + 1), ys.data());
2-party DCF scheme over F2 from the packed Half-Tree DPF (Grotto construction).
Definition half_tree_grotto_dcf.cuh:83
void Gen(cuda::std::span< int4 > cws, cuda::std::span< const int4, 2 > s0s, In a, uint64_t beta, size_t stride=1)
Generate split Cws for the packed point function.
Definition packed_half_tree_dpf.cuh:565
int4 Eval(bool b, int4 s0, cuda::std::span< const int4 > cws, In x, size_t stride=1)
Evaluate the packed output block containing x from split Cws.
Definition packed_half_tree_dpf.cuh:635
void EvalAll(bool b, int4 s0, cuda::std::span< const int4 > cws, int4 ys[], int par_depth=0, size_t stride=1)
Evaluate all packed output blocks from split Cws on the host.
Definition packed_half_tree_dpf.cuh:678
cudaError_t EvalAllGpu(bool b, int4 s0, cuda::std::span< const int4 > cws, cuda::std::span< int4 > ys, cudaStream_t stream=0, size_t stride=1) const
Launch full-domain CUDA evaluation from split device Cws.
Definition packed_half_tree_dpf.cuh:1204
static constexpr int kLanes
Definition packed_half_tree_dpf.cuh:134
void EvalRange(bool b, In first, In last, int4 s0, cuda::std::span< const int4 > cws, cuda::std::span< int4 > ys, int par_depth=0, size_t stride=1)
Evaluate the closed input interval [first, last] from split Cws.
Definition packed_half_tree_dpf.cuh:716
PackedHalfTreeDpf(Hash hash, int4 instance_seed)
Configure the CCR hash and public instance seed S.
Definition packed_half_tree_dpf.cuh:514
cudaError_t EvalAllGpuBatch(bool b, cuda::std::span< const int4 > s0s, cuda::std::span< const int4 > cws, cuda::std::span< int4 > ys, cudaStream_t stream=0, size_t stride=0) const
static constexpr int kDepth
Definition packed_half_tree_dpf.cuh:139
static uint64_t Extract(int4 block, In x)
Extracts the out_bits-bit output share for input x from a packed block.
Definition packed_half_tree_dpf.cuh:543
In Input
Definition packed_half_tree_dpf.cuh:167
static constexpr int kCwCount
Definition packed_half_tree_dpf.cuh:165
Single build-time switch for the deprecated v1 Cw-form compatibility entries.
Circular-correlation-robust (CCR) hash interface for HalfTree schemes.
Definition hash.cuh:86
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
2-party distributed point function (DPF) using the Half-Tree scheme.
Correction word for a tree level or the final packed-output correction word.
Definition packed_half_tree_dpf.cuh:159
int4 s
Tree seed correction word, or packed-output correction word in the final entry.
Definition packed_half_tree_dpf.cuh:160