Skip to content

Commit 558c4d3

Browse files
authored
Faster key comparison (#1464)
* Faster key comparison * comparitor comments * fix mustache code
1 parent 2fd4916 commit 558c4d3

File tree

4 files changed

+122
-4
lines changed

4 files changed

+122
-4
lines changed

include/glaze/json/read.hpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ namespace glz
168168
}
169169
}
170170

171-
if (compare<Length>(TargetKey.data(), it)) [[likely]] {
171+
if (comparitor<TargetKey>(it)) [[likely]] {
172172
it += Length;
173173
if (*it != '"') [[unlikely]] {
174174
if constexpr (Opts.error_on_unknown_keys) {

include/glaze/mustache/mustache.hpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ namespace glz
7575
[&]<size_t I>() {
7676
static constexpr auto TargetKey = get<I>(reflect<T>::keys);
7777
static constexpr auto Length = TargetKey.size();
78-
if ((Length == key.size()) && compare<Length>(TargetKey.data(), start)) [[likely]] {
78+
if ((Length == key.size()) && detail::comparitor<TargetKey>(start)) [[likely]] {
7979
if constexpr (detail::reflectable<T> && N > 0) {
8080
std::ignore = write<opt_true<Opts, &opts::raw>>(
8181
detail::get_member(value, get<I>(detail::to_tuple(value))), temp, ctx);

include/glaze/mustache/stencilcount.hpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ namespace glz
111111
[&]<size_t I>() {
112112
static constexpr auto TargetKey = get<I>(reflect<T>::keys);
113113
static constexpr auto Length = TargetKey.size();
114-
if ((Length == key.size()) && compare<Length>(TargetKey.data(), start)) [[likely]] {
114+
if ((Length == key.size()) && detail::comparitor<TargetKey>(start)) [[likely]] {
115115
if constexpr (detail::reflectable<T> && N > 0) {
116116
std::ignore = write<opt_true<Opts, &opts::raw>>(
117117
detail::get_member(value, get<I>(detail::to_tuple(value))), temp, ctx);

include/glaze/util/compare.hpp

+119-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
#include <string_view>
99
#include <type_traits>
1010

11-
namespace glz
11+
namespace glz::detail
1212
{
1313
template <class Char>
1414
inline bool compare(const Char* lhs, const Char* rhs, uint64_t count) noexcept
@@ -158,6 +158,124 @@ namespace glz
158158
return true;
159159
}
160160
}
161+
162+
template <size_t N>
163+
consteval auto bytes_to_unsigned_type() noexcept
164+
{
165+
if constexpr (N == 1) {
166+
return uint8_t{};
167+
}
168+
else if constexpr (N == 2) {
169+
return uint16_t{};
170+
}
171+
else if constexpr (N == 4) {
172+
return uint32_t{};
173+
}
174+
else if constexpr (N == 8) {
175+
return uint64_t{};
176+
}
177+
else {
178+
return;
179+
}
180+
}
181+
182+
template <size_t N>
183+
using unsigned_bytes_t = std::decay_t<decltype(bytes_to_unsigned_type<N>())>;
184+
185+
template <const std::string_view& Str, size_t N>
186+
requires (N <= 8)
187+
consteval auto pack()
188+
{
189+
using T = unsigned_bytes_t<N>;
190+
T v{};
191+
for (size_t i = 0; i < N; ++i) {
192+
v |= (static_cast<T>(uint8_t(Str[i])) << ((i % 8) * 8));
193+
}
194+
return v;
195+
}
196+
197+
template <const std::string_view& Str, size_t N>
198+
requires(N > 8)
199+
consteval auto pack() {
200+
constexpr auto chunks = N / 8;
201+
std::array<uint64_t, ((chunks > 0) ? chunks + 1 : 1)> v{};
202+
for (size_t i = 0; i < N; ++i) {
203+
const auto chunk = i / 8;
204+
v[chunk] |= (static_cast<uint64_t>(uint8_t(Str[i])) << ((i % 8) * 8));
205+
}
206+
return v;
207+
}
208+
209+
template <const std::string_view& Str, size_t N>
210+
requires (N <= 8)
211+
consteval auto pack_buffered()
212+
{
213+
using T = unsigned_bytes_t<N>;
214+
T v{};
215+
for (size_t i = 0; i < Str.size(); ++i) {
216+
v |= (static_cast<T>(uint8_t(Str[i])) << ((i % 8) * 8));
217+
}
218+
return v;
219+
}
220+
221+
template <const std::string_view& Str, size_t N = Str.size()>
222+
GLZ_ALWAYS_INLINE bool comparitor(const auto* other) noexcept
223+
{
224+
if constexpr (N == 8) {
225+
static constexpr auto packed = pack<Str, 8>();
226+
uint64_t in;
227+
std::memcpy(&in, other, 8);
228+
return (in == packed);
229+
}
230+
else if constexpr (N == 7) {
231+
static constexpr auto packed = pack_buffered<Str, 8>();
232+
uint64_t in{};
233+
std::memcpy(&in, other, 7);
234+
return (in == packed);
235+
}
236+
else if constexpr (N == 6) {
237+
static constexpr auto packed = pack_buffered<Str, 8>();
238+
uint64_t in{};
239+
std::memcpy(&in, other, 6);
240+
return (in == packed);
241+
}
242+
else if constexpr (N == 5) {
243+
static constexpr auto packed = pack<Str, 4>();
244+
uint32_t in;
245+
std::memcpy(&in, other, 4);
246+
return (in == packed) & (Str[4] == other[4]);
247+
}
248+
else if constexpr (N == 4) {
249+
static constexpr auto packed = pack<Str, 4>();
250+
uint32_t in;
251+
std::memcpy(&in, other, 4);
252+
return (in == packed);
253+
}
254+
else if constexpr (N == 3) {
255+
static constexpr auto packed = pack<Str, 2>();
256+
uint16_t in;
257+
std::memcpy(&in, other, 2);
258+
return (in == packed) & (Str[2] == other[2]);
259+
}
260+
else if constexpr (N == 2) {
261+
static constexpr auto packed = pack<Str, 2>();
262+
uint16_t in;
263+
std::memcpy(&in, other, 2);
264+
return (in == packed);
265+
}
266+
else if constexpr (N == 1) {
267+
return Str[0] == other[0];
268+
}
269+
else if constexpr (N == 0) {
270+
return true;
271+
}
272+
else {
273+
// Clang and GCC optimize this extremely well for constexpr std::string_view
274+
// Packing data can create more binary on GCC
275+
// The other cases probably aren't needed as compiler explorer shows them optimized equally well as memcmp
276+
return 0 == std::memcmp(Str.data(), other, N);
277+
}
278+
}
161279

162280
// compare_sv checks sizes
163281
inline constexpr bool compare_sv(const std::string_view lhs, const std::string_view rhs) noexcept

0 commit comments

Comments
 (0)