Pixie
Loading...
Searching...
No Matches
select_metadata_search.h
1#pragma once
2
3#include <pixie/bits.h>
4
5#include <algorithm>
6#include <bit>
7#include <cassert>
8#include <cstddef>
9#include <cstdint>
10#include <limits>
11#include <type_traits>
12
13#if defined(PIXIE_AVX512_SUPPORT) || defined(PIXIE_AVX2_SUPPORT)
14#include <immintrin.h>
15#endif
16
17namespace pixie::detail::compressed {
18
19// Return the first endpoint >= key, or count. Unaligned and partial arrays are
20// supported; masked loads never read beyond their logical storage. count is at
21// most 64 / sizeof(Endpoint), and key fits the unsigned endpoint type.
22template <class Endpoint>
23inline std::size_t first_metadata_ge(const Endpoint* endpoints,
24 std::size_t count,
25 std::uint64_t key) {
26 static_assert(std::is_unsigned_v<Endpoint> &&
27 (sizeof(Endpoint) == 2 || sizeof(Endpoint) == 4 ||
28 sizeof(Endpoint) == 8));
29 assert(count <= 64 / sizeof(Endpoint));
30 assert(key <= std::numeric_limits<Endpoint>::max());
31#ifdef PIXIE_AVX512_SUPPORT
32 if constexpr (sizeof(Endpoint) == 2) {
33 const auto valid = static_cast<__mmask32>((std::uint64_t{1} << count) - 1);
34 const auto values = _mm512_maskz_loadu_epi16(valid, endpoints);
35 const std::uint32_t matches =
36 valid & _mm512_cmpge_epu16_mask(values, _mm512_set1_epi16(key));
37 return matches == 0 ? count : std::countr_zero(matches);
38 } else if constexpr (sizeof(Endpoint) == 4) {
39 const auto valid = static_cast<__mmask16>((std::uint64_t{1} << count) - 1);
40 const auto values = _mm512_maskz_loadu_epi32(valid, endpoints);
41 const std::uint32_t matches =
42 valid & _mm512_cmpge_epu32_mask(values, _mm512_set1_epi32(key));
43 return matches == 0 ? count : std::countr_zero(matches);
44 } else {
45 const auto valid = static_cast<__mmask8>((std::uint64_t{1} << count) - 1);
46 const auto values = _mm512_maskz_loadu_epi64(valid, endpoints);
47 const std::uint32_t matches =
48 valid & _mm512_cmpge_epu64_mask(values, _mm512_set1_epi64(key));
49 return matches == 0 ? count : std::countr_zero(matches);
50 }
51#elif defined(PIXIE_AVX2_SUPPORT)
52 constexpr std::size_t lanes = 32 / sizeof(Endpoint);
53 std::size_t begin = 0;
54 while (count - begin >= lanes) {
55 const auto values =
56 _mm256_loadu_si256(reinterpret_cast<const __m256i*>(endpoints + begin));
57 std::uint32_t matches;
58 if constexpr (sizeof(Endpoint) == 2) {
59 const auto sign = _mm256_set1_epi16(-32768);
60 const auto less =
61 _mm256_cmpgt_epi16(_mm256_xor_si256(_mm256_set1_epi16(key), sign),
62 _mm256_xor_si256(values, sign));
63 matches = ~static_cast<std::uint32_t>(_mm256_movemask_epi8(less));
64 if (matches != 0) {
65 return begin + std::countr_zero(matches) / 2;
66 }
67 } else if constexpr (sizeof(Endpoint) == 4) {
68 const auto sign =
69 _mm256_set1_epi32(std::numeric_limits<std::int32_t>::min());
70 const auto less =
71 _mm256_cmpgt_epi32(_mm256_xor_si256(_mm256_set1_epi32(key), sign),
72 _mm256_xor_si256(values, sign));
73 matches = ~static_cast<std::uint32_t>(
74 _mm256_movemask_ps(_mm256_castsi256_ps(less))) &
75 0xff;
76 if (matches != 0) {
77 return begin + std::countr_zero(matches);
78 }
79 } else {
80 const auto sign =
81 _mm256_set1_epi64x(std::numeric_limits<std::int64_t>::min());
82 const auto less =
83 _mm256_cmpgt_epi64(_mm256_xor_si256(_mm256_set1_epi64x(key), sign),
84 _mm256_xor_si256(values, sign));
85 matches = ~static_cast<std::uint32_t>(
86 _mm256_movemask_pd(_mm256_castsi256_pd(less))) &
87 0xf;
88 if (matches != 0) {
89 return begin + std::countr_zero(matches);
90 }
91 }
92 begin += lanes;
93 }
94 for (; begin < count; ++begin) {
95 if (endpoints[begin] >= key) {
96 return begin;
97 }
98 }
99 return count;
100#else
101 for (std::size_t i = 0; i < count; ++i) {
102 if (endpoints[i] >= key) {
103 return i;
104 }
105 }
106 return count;
107#endif
108}
109
110template <class Endpoint>
111inline std::size_t grouped_lower_bound(const Endpoint* endpoints,
112 std::size_t count,
113 std::uint64_t key) {
114 constexpr std::size_t lanes = 64 / sizeof(Endpoint);
115 std::size_t low = 0;
116 std::size_t high = (count + lanes - 1) / lanes;
117 while (low < high) {
118 const auto mid = low + (high - low) / 2;
119 const auto end = std::min(count, (mid + 1) * lanes);
120 if (endpoints[end - 1] < key) {
121 low = mid + 1;
122 } else {
123 high = mid;
124 }
125 }
126 const auto begin = low * lanes;
127 return begin >= count
128 ? count
129 : begin + first_metadata_ge(endpoints + begin,
130 std::min(lanes, count - begin), key);
131}
132
133} // namespace pixie::detail::compressed