Skip to content

Commit 88aa396

Browse files
authored
Optimize parse_and_invoke (#1461)
* Optimize parse_and_invoke * Remove function indirection * cleaning * Add missing short circuit * Cleanup and visit * Make selected_index an optional parameter * add unlikely
1 parent 3d963a9 commit 88aa396

File tree

3 files changed

+50
-64
lines changed

3 files changed

+50
-64
lines changed

include/glaze/core/reflect.hpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -1780,7 +1780,7 @@ namespace glz::detail
17801780
const auto* c = quote_memchr<HashInfo.min_length>(it, end);
17811781
if (c) [[likely]] {
17821782
const auto n = size_t(static_cast<std::decay_t<decltype(it)>>(c) - it);
1783-
if (n == 0 || n > HashInfo.max_length) {
1783+
if (n == 0 || n > HashInfo.max_length) [[unlikely]] {
17841784
return N; // error
17851785
}
17861786

include/glaze/json/read.hpp

+47-61
Original file line numberDiff line numberDiff line change
@@ -140,9 +140,9 @@ namespace glz
140140
GLZ_MATCH_COLON(); \
141141
GLZ_SKIP_WS();
142142

143-
template <opts Opts, class T, size_t I, class Func, class Tuple, class Value>
143+
template <opts Opts, class T, size_t I, class Value, class... SelectedIndex>
144144
requires(glaze_object_t<T> || reflectable<T>)
145-
void decode_index(Func&& func, Tuple&& tuple, Value&& value, is_context auto&& ctx, auto&& it, auto&& end)
145+
void decode_index(Value&& value, is_context auto&& ctx, auto&& it, auto&& end, SelectedIndex&&... selected_index)
146146
{
147147
static constexpr auto TargetKey = glz::get<I>(reflect<T>::keys);
148148
static constexpr auto Length = TargetKey.size();
@@ -200,13 +200,31 @@ namespace glz
200200
GLZ_SKIP_WS();
201201
GLZ_MATCH_COLON();
202202
GLZ_SKIP_WS();
203+
204+
using V = refl_t<T, I>;
203205

204-
// invoke on the value
205-
if constexpr (glaze_object_t<T>) {
206-
std::forward<Func>(func)(get<I>(reflect<T>::values), I);
206+
if constexpr (const_value_v<V>) {
207+
if constexpr (Opts.error_on_const_read) {
208+
ctx.error = error_code::attempt_const_read;
209+
}
210+
else {
211+
// do not read anything into the const value
212+
skip_value<JSON>::op<Opts>(ctx, it, end);
213+
}
207214
}
208215
else {
209-
std::forward<Func>(func)(get<I>(std::forward<Tuple>(tuple)), I);
216+
if constexpr (glaze_object_t<T>) {
217+
from<JSON, std::remove_cvref_t<V>>::template op<ws_handled<Opts>()>(
218+
get_member(value, get<I>(reflect<T>::values)), ctx, it, end);
219+
}
220+
else {
221+
from<JSON, std::remove_cvref_t<V>>::template op<ws_handled<Opts>()>(
222+
get_member(value, get<I>(to_tuple(value))), ctx, it, end);
223+
}
224+
}
225+
226+
if constexpr (Opts.error_on_missing_keys || is_partial_read<T> || Opts.partial_read) {
227+
((selected_index = I), ...);
210228
}
211229
}
212230
else [[unlikely]] {
@@ -258,10 +276,10 @@ namespace glz
258276
}
259277
}
260278

261-
template <opts Opts, class T, auto& HashInfo, class Func, class Value>
279+
template <opts Opts, class T, auto& HashInfo, class Value, class... SelectedIndex>
262280
requires(glaze_object_t<T> || reflectable<T>)
263-
GLZ_ALWAYS_INLINE constexpr void parse_and_invoke(Func&& func, Value&& value, is_context auto&& ctx, auto&& it,
264-
auto&& end)
281+
GLZ_ALWAYS_INLINE constexpr void parse_and_invoke(Value&& value, is_context auto&& ctx, auto&& it,
282+
auto&& end, SelectedIndex&&... selected_index)
265283
{
266284
constexpr auto type = HashInfo.type;
267285
constexpr auto N = reflect<T>::size;
@@ -271,12 +289,7 @@ namespace glz
271289
}
272290

273291
if constexpr (N == 1) {
274-
if constexpr (glaze_object_t<T>) {
275-
decode_index<Opts, T, 0>(func, nullptr, value, ctx, it, end);
276-
}
277-
else {
278-
decode_index<Opts, T, 0>(func, to_tuple(value), value, ctx, it, end);
279-
}
292+
decode_index<Opts, T, 0>(value, ctx, it, end, selected_index...);
280293
}
281294
else {
282295
const auto index = decode_hash<JSON, T, HashInfo, HashInfo.type>::op(it, end);
@@ -303,23 +316,10 @@ namespace glz
303316
}
304317
}
305318

306-
// We see better performance with an array of function pointers than a glz::jump_table here.
307-
if constexpr (glaze_object_t<T>) {
308-
static constexpr auto decoders = [&]<size_t... I>(std::index_sequence<I...>) constexpr {
309-
return std::array{&decode_index<Opts, T, I, decltype(func), decltype(nullptr), decltype(value),
310-
decltype(ctx), decltype(it), decltype(end)>...};
311-
}(std::make_index_sequence<N>{});
312-
313-
decoders[index](std::forward<Func>(func), nullptr, value, ctx, it, end);
314-
}
315-
else {
316-
static constexpr auto decoders = [&]<size_t... I>(std::index_sequence<I...>) constexpr {
317-
return std::array{&decode_index<Opts, T, I, decltype(func), decltype(to_tuple(value)),
318-
decltype(value), decltype(ctx), decltype(it), decltype(end)>...};
319-
}(std::make_index_sequence<N>{});
320-
321-
decoders[index](std::forward<Func>(func), to_tuple(value), value, ctx, it, end);
322-
}
319+
// We see better performance function pointers than a glz::jump_table here.
320+
visit<N>([&]<size_t I>() {
321+
decode_index<Opts, T, I>(value, ctx, it, end, selected_index...);
322+
}, index);
323323
}
324324
}
325325

@@ -1966,35 +1966,21 @@ namespace glz
19661966
it = start; // reset the iterator
19671967
}
19681968
}
1969-
1970-
parse_and_invoke<Opts, T, hash_info<T>>(
1971-
[&](auto&& element, const size_t index) {
1972-
if constexpr (Opts.error_on_missing_keys || is_partial_read<T> || Opts.partial_read) {
1973-
fields[index] = true;
1974-
}
1975-
else {
1976-
(void)index;
1977-
}
1978-
1979-
using V = decltype(get_member(value, element));
1980-
1981-
if constexpr (const_value_v<V>) {
1982-
if constexpr (Opts.error_on_const_read) {
1983-
ctx.error = error_code::attempt_const_read;
1984-
}
1985-
else {
1986-
// do not read anything into the const value
1987-
skip_value<JSON>::op<Opts>(ctx, it, end);
1988-
}
1989-
}
1990-
else {
1991-
from<JSON, std::remove_cvref_t<V>>::template op<ws_handled<Opts>()>(
1992-
get_member(value, element), ctx, it, end);
1993-
}
1994-
},
1995-
value, ctx, it, end);
1996-
if (bool(ctx.error)) [[unlikely]]
1997-
return;
1969+
1970+
if constexpr (Opts.error_on_missing_keys || is_partial_read<T> || Opts.partial_read) {
1971+
size_t index = num_members;
1972+
parse_and_invoke<Opts, T, hash_info<T>>(value, ctx, it, end, index);
1973+
if (bool(ctx.error)) [[unlikely]]
1974+
return;
1975+
if (index < num_members) {
1976+
fields[index] = true;
1977+
}
1978+
}
1979+
else {
1980+
parse_and_invoke<Opts, T, hash_info<T>>(value, ctx, it, end);
1981+
if (bool(ctx.error)) [[unlikely]]
1982+
return;
1983+
}
19981984
}
19991985
else {
20001986
// For types like std::map, std::unordered_map

include/glaze/util/for_each.hpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ namespace glz::detail
8787

8888
#define GLZ_CASE(I) \
8989
case I: { \
90-
static_cast<Lambda&&>(lambda).template operator()<I>(); \
90+
lambda.template operator()<I>(); \
9191
break; \
9292
}
9393

@@ -116,7 +116,7 @@ namespace glz::detail
116116
return;
117117
}
118118
else if constexpr (N == 1) {
119-
static_cast<Lambda&&>(lambda).template operator()<0>();
119+
lambda.template operator()<0>();
120120
}
121121
GLZ_SWITCH(2, 0, 1)
122122
GLZ_SWITCH(3, 0, 1, 2)

0 commit comments

Comments
 (0)