From 1a2828f36c24215fc4e336bb79dfd975356c6f55 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Sat, 7 Nov 2026 23:44:11 +0100 Subject: [PATCH] Iterator: test for objects before arrays #5835 replaced the switch on the value type in operator*, operator->, operator++, operator-- and operator== of iter_impl with an if chain that tests for an array first. Every step over an object then pays for a failed test and a taken branch: with the iterator inlined, walking twitter.json took 14% more cycles than with 3.12.0, and citm_catalog.json 3.5% more. Test for objects first. Walking every value of the three files now takes fewer cycles than with 3.12.0 (Apple M1 Max, Apple clang 21): citm_catalog.json 1.21x, twitter.json 1.04x, canada.json 1.17x as fast. canada.json, which holds only arrays, gives back part of the gain of #5835 (develop: 1.62x); the other orders tried (arrays first without hints, objects first without hints, a switch) each left one of the files slower than 3.12.0. Signed-off-by: Niels Lohmann --- .../nlohmann/detail/iterators/iter_impl.hpp | 62 ++++++++++--------- single_include/nlohmann/json.hpp | 62 ++++++++++--------- 2 files changed, 66 insertions(+), 58 deletions(-) diff --git a/include/nlohmann/detail/iterators/iter_impl.hpp b/include/nlohmann/detail/iterators/iter_impl.hpp index 6e6cc0ef6..6a0d2cef5 100644 --- a/include/nlohmann/detail/iterators/iter_impl.hpp +++ b/include/nlohmann/detail/iterators/iter_impl.hpp @@ -277,18 +277,22 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci { JSON_ASSERT(m_object != nullptr); - if (JSON_HEDLEY_LIKELY(m_object->m_data.m_type == value_t::array)) - { - JSON_ASSERT(m_it.array_iterator != m_object->m_data.m_value.array->end()); - return *m_it.array_iterator; - } - + // objects are tested before arrays here and in the other operators: + // with arrays first, walking a document of objects such as + // twitter.json took 14% more cycles than with 3.12.0's switch; with + // objects first, no document walks slower than with 3.12.0 if (JSON_HEDLEY_LIKELY(m_object->m_data.m_type == value_t::object)) { JSON_ASSERT(m_it.object_iterator != m_object->m_data.m_value.object->end()); return m_it.object_iterator->second; } + if (JSON_HEDLEY_LIKELY(m_object->m_data.m_type == value_t::array)) + { + JSON_ASSERT(m_it.array_iterator != m_object->m_data.m_value.array->end()); + return *m_it.array_iterator; + } + // neither array nor object: a primitive value (null has no value to point to) if (JSON_HEDLEY_LIKELY(m_object->m_data.m_type != value_t::null && m_it.primitive_iterator.is_begin())) { @@ -306,18 +310,18 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci { JSON_ASSERT(m_object != nullptr); - if (JSON_HEDLEY_LIKELY(m_object->m_data.m_type == value_t::array)) - { - JSON_ASSERT(m_it.array_iterator != m_object->m_data.m_value.array->end()); - return &*m_it.array_iterator; - } - if (JSON_HEDLEY_LIKELY(m_object->m_data.m_type == value_t::object)) { JSON_ASSERT(m_it.object_iterator != m_object->m_data.m_value.object->end()); return &(m_it.object_iterator->second); } + if (JSON_HEDLEY_LIKELY(m_object->m_data.m_type == value_t::array)) + { + JSON_ASSERT(m_it.array_iterator != m_object->m_data.m_value.array->end()); + return &*m_it.array_iterator; + } + // neither array nor object: a primitive value if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin())) { @@ -346,18 +350,18 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci { JSON_ASSERT(m_object != nullptr); - if (JSON_HEDLEY_LIKELY(m_object->m_data.m_type == value_t::array)) - { - ++m_it.array_iterator; - return *this; - } - if (JSON_HEDLEY_LIKELY(m_object->m_data.m_type == value_t::object)) { std::advance(m_it.object_iterator, 1); return *this; } + if (JSON_HEDLEY_LIKELY(m_object->m_data.m_type == value_t::array)) + { + ++m_it.array_iterator; + return *this; + } + // neither array nor object: a primitive value ++m_it.primitive_iterator; return *this; @@ -382,18 +386,18 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci { JSON_ASSERT(m_object != nullptr); - if (JSON_HEDLEY_LIKELY(m_object->m_data.m_type == value_t::array)) - { - --m_it.array_iterator; - return *this; - } - if (JSON_HEDLEY_LIKELY(m_object->m_data.m_type == value_t::object)) { std::advance(m_it.object_iterator, -1); return *this; } + if (JSON_HEDLEY_LIKELY(m_object->m_data.m_type == value_t::array)) + { + --m_it.array_iterator; + return *this; + } + // neither array nor object: a primitive value --m_it.primitive_iterator; return *this; @@ -415,16 +419,16 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci return true; } - if (JSON_HEDLEY_LIKELY(m_object->m_data.m_type == value_t::array)) - { - return (m_it.array_iterator == other.m_it.array_iterator); - } - if (JSON_HEDLEY_LIKELY(m_object->m_data.m_type == value_t::object)) { return (m_it.object_iterator == other.m_it.object_iterator); } + if (JSON_HEDLEY_LIKELY(m_object->m_data.m_type == value_t::array)) + { + return (m_it.array_iterator == other.m_it.array_iterator); + } + // neither array nor object: a primitive value return (m_it.primitive_iterator == other.m_it.primitive_iterator); } diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index f91671e82..1ed74f706 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -24506,18 +24506,22 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci { JSON_ASSERT(m_object != nullptr); - if (JSON_HEDLEY_LIKELY(m_object->m_data.m_type == value_t::array)) - { - JSON_ASSERT(m_it.array_iterator != m_object->m_data.m_value.array->end()); - return *m_it.array_iterator; - } - + // objects are tested before arrays here and in the other operators: + // with arrays first, walking a document of objects such as + // twitter.json took 14% more cycles than with 3.12.0's switch; with + // objects first, no document walks slower than with 3.12.0 if (JSON_HEDLEY_LIKELY(m_object->m_data.m_type == value_t::object)) { JSON_ASSERT(m_it.object_iterator != m_object->m_data.m_value.object->end()); return m_it.object_iterator->second; } + if (JSON_HEDLEY_LIKELY(m_object->m_data.m_type == value_t::array)) + { + JSON_ASSERT(m_it.array_iterator != m_object->m_data.m_value.array->end()); + return *m_it.array_iterator; + } + // neither array nor object: a primitive value (null has no value to point to) if (JSON_HEDLEY_LIKELY(m_object->m_data.m_type != value_t::null && m_it.primitive_iterator.is_begin())) { @@ -24535,18 +24539,18 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci { JSON_ASSERT(m_object != nullptr); - if (JSON_HEDLEY_LIKELY(m_object->m_data.m_type == value_t::array)) - { - JSON_ASSERT(m_it.array_iterator != m_object->m_data.m_value.array->end()); - return &*m_it.array_iterator; - } - if (JSON_HEDLEY_LIKELY(m_object->m_data.m_type == value_t::object)) { JSON_ASSERT(m_it.object_iterator != m_object->m_data.m_value.object->end()); return &(m_it.object_iterator->second); } + if (JSON_HEDLEY_LIKELY(m_object->m_data.m_type == value_t::array)) + { + JSON_ASSERT(m_it.array_iterator != m_object->m_data.m_value.array->end()); + return &*m_it.array_iterator; + } + // neither array nor object: a primitive value if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin())) { @@ -24575,18 +24579,18 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci { JSON_ASSERT(m_object != nullptr); - if (JSON_HEDLEY_LIKELY(m_object->m_data.m_type == value_t::array)) - { - ++m_it.array_iterator; - return *this; - } - if (JSON_HEDLEY_LIKELY(m_object->m_data.m_type == value_t::object)) { std::advance(m_it.object_iterator, 1); return *this; } + if (JSON_HEDLEY_LIKELY(m_object->m_data.m_type == value_t::array)) + { + ++m_it.array_iterator; + return *this; + } + // neither array nor object: a primitive value ++m_it.primitive_iterator; return *this; @@ -24611,18 +24615,18 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci { JSON_ASSERT(m_object != nullptr); - if (JSON_HEDLEY_LIKELY(m_object->m_data.m_type == value_t::array)) - { - --m_it.array_iterator; - return *this; - } - if (JSON_HEDLEY_LIKELY(m_object->m_data.m_type == value_t::object)) { std::advance(m_it.object_iterator, -1); return *this; } + if (JSON_HEDLEY_LIKELY(m_object->m_data.m_type == value_t::array)) + { + --m_it.array_iterator; + return *this; + } + // neither array nor object: a primitive value --m_it.primitive_iterator; return *this; @@ -24644,16 +24648,16 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci return true; } - if (JSON_HEDLEY_LIKELY(m_object->m_data.m_type == value_t::array)) - { - return (m_it.array_iterator == other.m_it.array_iterator); - } - if (JSON_HEDLEY_LIKELY(m_object->m_data.m_type == value_t::object)) { return (m_it.object_iterator == other.m_it.object_iterator); } + if (JSON_HEDLEY_LIKELY(m_object->m_data.m_type == value_t::array)) + { + return (m_it.array_iterator == other.m_it.array_iterator); + } + // neither array nor object: a primitive value return (m_it.primitive_iterator == other.m_it.primitive_iterator); }