Skip to content

Index the large objects of json_view - #5629

Closed
nlohmann wants to merge 9 commits into
json-view/16-view-simdfrom
json-view/18-view-object-index
Closed

nlohmann wants to merge 9 commits into
json-view/16-view-simdfrom
json-view/18-view-object-index

Conversation

@nlohmann

@nlohmann nlohmann commented Sep 29, 2026 •

Copy link
Copy Markdown
Owner

Part of the stack for the zero-copy view (#5295).

Summary

Lookups in objects are linear, as for ordered_json. Objects with 128 members or more now get a hash table after parsing:

  • open addressing;
  • of duplicate keys, the first is kept, as for the linear search;
  • operator[], at(), find(), contains(), count(), value(), and JSON pointers take constant time on average in such objects.

The idea of switching to a hash table for large objects comes from Boost.JSON.

The parser notes a large object when it closes it. This happens out of line, so the parse loop only has a call for it; done inline, it made canada.json 37% slower. The object node keeps the number of its table.

The node index section of the architecture page describes the tables and what extra holds for objects.

Benchmarks

  • Looking up every key of an object with 10,000 members: 59.8 ms → 0.16 ms.
  • Parsing (json_document::parse, best of 7, separate processes): most files within 1%; canada +5%, mesh.pretty +3%, citm +3%.

Tests

  • Objects with 127, 128, 129, and 10,000 members.
  • Escaped, empty, duplicate, and missing keys, and comparisons.
  • Nested large objects, and documents reused with read().

Public API

No change (faster lookups).


Written by Claude Code.

🤖 Generated with Claude Code

@nlohmann
nlohmann added this pull request to stack #5636 September 29, 2026 14:20
@nlohmann nlohmann changed the title json view/18 view object index Index the large objects of json_view Sep 29, 2026
@nlohmann

Copy link
Copy Markdown
Owner Author

CI fixes for this PR (commits fa58c6e and 448c9dd, plus the lower fixes merged in). CI had not run yet, so these were found locally with the CI's compilers:

  • ci_test_gcc: -Werror=useless-cast on static_cast<std::size_t>(key_hash(...)) (twice) in detail/view/object_index.hpp. The call returns a std::uint64_t prvalue, which is std::size_t on Linux x86-64. The hash is now stored in a variable and cast from there.
  • old clang (ci_test_compilers_clang 3.x): with the new nested struct object_index, clang 4 rejects document_data() noexcept = default; with "default member initializer for 'indexes' needed within definition of enclosing class". Nothing depends on that constructor being noexcept, so it now takes the implicit exception specification (document_data() = default;). Reproduced with silkeh/clang:4. The same error reappeared for every upper branch before this fix.

Verified with gcc:16 (linux/amd64, CI flags, C++11/C++20, plus a JSON_NOEXCEPTION build run as --no-throw) and with silkeh/clang:4 (syntax check of all view tests, up to #5635).

— posted by Claude Code on behalf of @nlohmann

@nlohmann
nlohmann removed this pull request from stack #5636 September 30, 2026 13:19
@nlohmann
nlohmann force-pushed the json-view/16-view-simd branch from 50f0a1b to d33586c Compare September 30, 2026 13:20
@nlohmann
nlohmann force-pushed the json-view/18-view-object-index branch from 448c9dd to 0f6438a Compare September 30, 2026 13:20
@nlohmann
nlohmann added this pull request to stack #5739 September 30, 2026 13:21
@nlohmann
nlohmann force-pushed the json-view/16-view-simd branch from d33586c to 6f43699 Compare September 30, 2026 15:34
@nlohmann
nlohmann force-pushed the json-view/18-view-object-index branch 3 times, most recently from fc025bb to b02a0bd Compare September 30, 2026 18:06
@nlohmann nlohmann added the review needed It would be great if someone could review the proposed changes. label Sep 30, 2026
@nlohmann
nlohmann marked this pull request as ready for review September 30, 2026 18:16
Lookups in objects are linear, as for ordered_json. Objects with 128
members or more now get a hash table after parsing (open addressing; the
first of duplicate keys is kept, as for the linear search), so that
operator[], at(), find(), contains(), count(), value(), and JSON pointers
take constant time on average in them; the idea of switching to a hash
table for large objects is Boost.JSON's. The parser notes such objects when
it closes them (out of line, so that the parse loop only has a call for
it), and the object node keeps the number of its table.

Looking up each key of an object with 10,000 members: 59.8 ms -> 0.16 ms.
Parsing (json_document::parse, best of 7, separate processes): most files
within 1%; canada +5%, mesh.pretty +3%, citm +3%.

Tests: objects with 127, 128, 129, and 10,000 members (escaped, empty,
and duplicate keys, missing keys, comparisons), nested large objects, and
documents reused with read().

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
The node index section says what extra holds for objects and how large
objects are indexed.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
GCC -Werror=useless-cast on Linux x86-64 rejects
static_cast<std::size_t>(key_hash(...)): the call returns a
std::uint64_t prvalue, the same type as std::size_t there, while the cast
is needed where std::size_t is 32 bits wide. Store the hash in a variable
and cast that, which GCC does not report.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
With the nested struct object_index, clang 4 (and, by the same bug, the
clang 3.x of ci_test_compilers_clang) rejects the explicitly noexcept
defaulted constructor: "default member initializer for 'indexes' needed
within definition of enclosing class 'document_data' outside of member
functions". Nothing depends on the constructor being noexcept, so let it
take the implicit exception specification.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
@nlohmann
nlohmann force-pushed the json-view/16-view-simd branch from d4ac1b7 to 1f0c3be Compare September 30, 2026 19:19
@nlohmann
nlohmann force-pushed the json-view/18-view-object-index branch from b02a0bd to eede67c Compare September 30, 2026 19:19
…ndex

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
…ndex

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
…ndex

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
@nlohmann
nlohmann removed this pull request from stack #5739 October 6, 2026 09:26
@nlohmann

nlohmann commented Oct 6, 2026

Copy link
Copy Markdown
Owner Author

The zero-copy view stack (#5295) was rebased onto develop and regrouped from 20 PRs into 10, one squashed commit each. The changes of this PR are now in #5628; its review comments stay here for reference.


Written by Claude Code.

@nlohmann nlohmann closed this Oct 6, 2026
@nlohmann
nlohmann deleted the json-view/18-view-object-index branch October 12, 2026 20:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation L review needed It would be great if someone could review the proposed changes. tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant