Everett
Loading...
Searching...
No Matches
rank15.h
Go to the documentation of this file.
1
13#pragma once
14
15#include <everett/backend.h>
17#include <simd/integer.h>
18
19#include <everett/word_view.h>
20
21#include <array>
22#include <bit>
23#include <cstdint>
24#include <span>
25#include <stdexcept>
26#include <vector>
27
28
29namespace everett {
30 // Separate packed-class codec: 15-entry groups do not align with the
31 // 512/2048 source-bit runs of rank.h. We retain four bits per class and one
32 // 64-bit checkpoint every 128 classes. Queries accumulate at most eight
33 // packed words and perform one horizontal sum.
34 // There is no full origin bitvector and no arbitrary within-group rank.
35 // Views check section shapes, not the semantic consistency of borrowed
36 // classes/checkpoints; those must come from a builder or validated reader.
37 struct rank15_view {
38 rank15_view(std::span<std::uint64_t const> classes,
39 std::span<std::uint64_t const> checkpoints,
40 std::uint64_t virtual_count)
41 : rank15_view(word_view(classes), word_view(checkpoints), virtual_count) {}
42
43 template <class Words> requires std::is_same_v<Words, word_view>
44 rank15_view(Words classes, Words checkpoints,
45 std::uint64_t virtual_count)
46 : classes_(classes), checkpoints_(checkpoints),
47 virtual_count_(virtual_count),
48 group_count_(virtual_count / 15 + (virtual_count % 15 != 0)) {
49 auto groups = group_count();
50 if (classes.size() != ((groups + 15) >> 4) ||
51 checkpoints.size() != ((groups + 127) >> 7))
52 error_detail::raise<std::invalid_argument>("invalid rank15 spans");
53 }
54
55 std::uint64_t size() const noexcept { return virtual_count_; }
56 std::uint64_t group_count() const noexcept { return group_count_; }
57 // Derived from the final real group; no endpoint checkpoint is stored.
58 template <simd::architecture Arch = simd::scalar>
59 std::uint64_t count() const {
60 if (!group_count()) return 0;
61 auto last = group_count() - 1;
62 auto population = class_at(last);
63 if (population > virtual_count_ - last * 15)
64 error_detail::raise<std::invalid_argument>("invalid rank15 final population");
65 return add_prefix(rank<Arch>(last), population, virtual_count_);
66 }
67
68 word_view class_words() const noexcept { return classes_; }
69 word_view checkpoint_words() const noexcept { return checkpoints_; }
70
71 unsigned class_at(std::uint64_t group) const {
72 if (group >= group_count()) error_detail::raise<std::out_of_range>("rank15 class");
73 return unsigned((classes_[group >> 4] >> (4 * (group & 15))) & 15);
74 }
75
76 // rank(group) counts entries before the start of an existing group.
77 // An empty index has no valid rank query; count() handles its total.
78 template <simd::architecture Arch = simd::scalar>
79 std::uint64_t rank(std::uint64_t group) const {
80 if (group >= group_count()) error_detail::raise<std::out_of_range>("rank15 group");
81 auto result = checkpoints_[group >> 7];
82 auto limit = virtual_count_;
83 if ((group & 127) == 0) return add_prefix(result, 0, limit);
84 auto word = (group >> 7) << 3;
85 if constexpr (!std::same_as<Arch, simd::scalar> && std::endian::native == std::endian::little)
86 if (classes_.size() - word >= 8)
87 return add_prefix(result, unsigned(reduce_add_widened(prefix_vectors<Arch>(
88 classes_.bytes().data() + word * 8, unsigned(group & 127)))), limit);
89 // Each word contributes at most 30 to each byte. Eight words fit in
90 // byte lanes (240), so we can accumulate before the horizontal sum.
91 std::uint64_t pairs = 0;
92 for (; word < (group >> 4); ++word) pairs += pair_nibbles(classes_[word]);
93 auto tail = unsigned(group & 15);
94 // group is an existing class, so this word exists even for tail=0.
95 pairs += pair_nibbles(classes_[word] & ((std::uint64_t{1} << (4 * tail)) - 1));
96 return add_prefix(result, sum_bytes(pairs), limit);
97 }
98
99 private:
100 static std::uint64_t add_prefix(std::uint64_t checkpoint, unsigned prefix, std::uint64_t limit) {
101 if (checkpoint > limit || prefix > limit - checkpoint)
102 error_detail::raise<std::invalid_argument>("invalid rank15 checkpoint or prefix");
103 return checkpoint + prefix;
104 }
105 template <simd::architecture Arch, unsigned Vector = 0>
106 static simd_inline auto prefix_vectors(std::byte const * words, unsigned count) noexcept {
107 constexpr auto width = backend_detail::register_bytes<Arch>;
108 using V = simd::vec<std::uint8_t, width, Arch>;
109 constexpr auto positions = [=] {
110 std::array<std::uint8_t, width> result{};
111 for (unsigned i = 0; i < result.size(); ++i) result[i] = 2 * (Vector * width + i);
112 return result;
113 }();
114 auto packed = V::loadu(reinterpret_cast<std::uint8_t const *>(words) + Vector * width);
115 auto low = packed & V(15);
116 auto high = packed.template right<4>();
117 auto boundary = V(count);
118 auto pairs = select(V(positions) < boundary, low, V(0)) +
119 select(V(positions) + V(1) < boundary, high, V(0));
120 // At most four registers contribute: each byte sum is <=120. The
121 // caller widens exactly once before reducing the <=1920 total.
122 if constexpr ((Vector + 1) * width == 64) return pairs;
123 else return pairs + prefix_vectors<Arch, Vector + 1>(words, count);
124 }
125
126 static std::uint64_t pair_nibbles(std::uint64_t value) noexcept {
127 return (value & 0x0f0f0f0f0f0f0f0full) + ((value >> 4) & 0x0f0f0f0f0f0f0f0full);
128 }
129
130 static unsigned sum_bytes(std::uint64_t value) noexcept {
131 // Byte lanes are at most 240. Widen before the horizontal sum: four
132 // 16-bit lanes and their total (at most 1920) fit without carries.
133 value = (value & 0x00ff00ff00ff00ffull) + ((value >> 8) & 0x00ff00ff00ff00ffull);
134 return unsigned((value * 0x0001000100010001ull) >> 48);
135 }
136
139 std::uint64_t virtual_count_;
140 std::uint64_t group_count_;
141 };
142
144 static rank15_index build(std::span<std::uint8_t const> source, std::uint64_t count) {
145 auto groups = count / 15 + (count % 15 != 0);
146 if (source.size() != groups) error_detail::raise<std::invalid_argument>("rank15 class length");
147 rank15_index result;
148 result.virtual_count = count;
149 result.classes.resize((groups + 15) >> 4);
150 std::uint64_t total = 0;
151 for (std::uint64_t i = 0; i < groups; ++i) {
152 auto limit = i + 1 == groups && count % 15 ? count % 15 : 15;
153 if (source[i] > limit) error_detail::raise<std::invalid_argument>("rank15 class population");
154 if ((i & 127) == 0) result.checkpoints.push_back(total);
155 result.classes[i >> 4] |= std::uint64_t(source[i]) << (4 * (i & 15));
156 total += source[i];
157 }
158 return result;
159 }
160
162
163 rank15_view view() const && = delete;
164
165 std::vector<std::uint64_t> classes;
166 std::vector<std::uint64_t> checkpoints;
167 std::uint64_t virtual_count = 0;
168 };
169}
Shares explicit SIMD architecture traits with Everett's kernels.
Outlines exceptional check failures while preserving their types and messages.
Definition active_engine.h:18
Definition rank15.h:143
static rank15_index build(std::span< std::uint8_t const > source, std::uint64_t count)
Definition rank15.h:144
std::vector< std::uint64_t > classes
Definition rank15.h:165
rank15_view view() const &
Definition rank15.h:161
rank15_view view() const &&=delete
std::uint64_t virtual_count
Definition rank15.h:167
std::vector< std::uint64_t > checkpoints
Definition rank15.h:166
Definition rank15.h:37
std::uint64_t rank(std::uint64_t group) const
Definition rank15.h:79
unsigned class_at(std::uint64_t group) const
Definition rank15.h:71
std::uint64_t group_count_
Definition rank15.h:140
word_view checkpoints_
Definition rank15.h:138
static auto prefix_vectors(std::byte const *words, unsigned count) noexcept
Definition rank15.h:106
rank15_view(std::span< std::uint64_t const > classes, std::span< std::uint64_t const > checkpoints, std::uint64_t virtual_count)
Definition rank15.h:38
word_view class_words() const noexcept
Definition rank15.h:68
std::uint64_t group_count() const noexcept
Definition rank15.h:56
std::uint64_t size() const noexcept
Definition rank15.h:55
word_view classes_
Definition rank15.h:137
std::uint64_t virtual_count_
Definition rank15.h:139
std::uint64_t count() const
Definition rank15.h:59
static std::uint64_t pair_nibbles(std::uint64_t value) noexcept
Definition rank15.h:126
rank15_view(Words classes, Words checkpoints, std::uint64_t virtual_count)
Definition rank15.h:44
static unsigned sum_bytes(std::uint64_t value) noexcept
Definition rank15.h:130
word_view checkpoint_words() const noexcept
Definition rank15.h:69
static std::uint64_t add_prefix(std::uint64_t checkpoint, unsigned prefix, std::uint64_t limit)
Definition rank15.h:100
Definition word_view.h:31
std::size_t size() const noexcept
Definition word_view.h:41
std::span< std::byte const > bytes() const noexcept
Definition word_view.h:43
Borrows native or little-endian directory words and select samples.