Files
json/tests/benchmarks/src/benchmarks_view.cpp
Niels Lohmann 4303ba674d Add json_document and json_view: node index, parser, and document
Add json_document and json_view, a read-only, zero-copy index of a
JSON text, as the first public slice of the zero-copy view (#5295).

A parse produces a flat array of 16-byte nodes in document order,
one per value and one per object key. Strings stay in the source
text; escaped strings are decoded into an arena. Integers are
converted while their digits are in the cache; floats keep only
their digit layout and are converted on read. Containers store the
size of their subtree, so a reader can step over one in constant
time. A document makes a handful of allocations, however many
values it has.

The parser accepts exactly what json::parse accepts, with every
combination of ignore_comments and ignore_trailing_commas, with and
without a trailing NUL, and under JSON_STRICT_NUL_HANDLING. It is
portable C++11 and does not depend on byte order.

basic_json_document adds parse, parse_copy, accept, read (reuses a
document's memory), root, is_discarded, source, owns_source,
node_count, memory_usage, and shrink_to_fit. basic_json_view adds
type, the is_* queries, operator bool, size, empty, materialize,
and source_offset. A parse error throws the same exception
basic_json::parse would throw for the same input, message and
position included.

detail::abi_config keeps JSON_STRICT_NUL_HANDLING and
JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON readable after json.hpp
undefines them, in the ABI namespace so they always match the
basic_json in use.

A NUL byte that ends a // comment is the end of the input, as in
parse() since #5696.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-06 10:48:06 +02:00

147 lines
5.3 KiB
C++

// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++ (supporting code)
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
// Benchmarks of json_document (nlohmann/json_view.hpp). They use the inputs of
// ParseString and ParseIndented in benchmarks.cpp, so that each ViewParse row
// can be read against the json::parse row of the same file.
#include <benchmark/benchmark.h>
#include <nlohmann/json_view.hpp>
#include <fstream>
#include <iterator>
#include <string>
#include <test_data.hpp>
using json = nlohmann::json;
using json_document = nlohmann::json_document;
static std::string read_file(const char* filename)
{
std::ifstream f(filename, std::ios::binary);
return std::string((std::istreambuf_iterator<char>(f)), std::istreambuf_iterator<char>());
}
#define JSON_VIEW_BENCHMARK_FILES(fn) \
BENCHMARK_CAPTURE(fn, jeopardy, TEST_DATA_DIRECTORY "/jeopardy/jeopardy.json"); \
BENCHMARK_CAPTURE(fn, canada, TEST_DATA_DIRECTORY "/nativejson-benchmark/canada.json"); \
BENCHMARK_CAPTURE(fn, citm_catalog, TEST_DATA_DIRECTORY "/nativejson-benchmark/citm_catalog.json"); \
BENCHMARK_CAPTURE(fn, twitter, TEST_DATA_DIRECTORY "/nativejson-benchmark/twitter.json"); \
BENCHMARK_CAPTURE(fn, floats, TEST_DATA_DIRECTORY "/regression/floats.json"); \
BENCHMARK_CAPTURE(fn, signed_ints, TEST_DATA_DIRECTORY "/regression/signed_ints.json"); \
BENCHMARK_CAPTURE(fn, unsigned_ints, TEST_DATA_DIRECTORY "/regression/unsigned_ints.json"); \
BENCHMARK_CAPTURE(fn, small_signed_ints, TEST_DATA_DIRECTORY "/regression/small_signed_ints.json")
//////////////////////////////////////////////////////////////////////////////
// parse into a new document (compare with ParseString)
//////////////////////////////////////////////////////////////////////////////
static void ViewParse(benchmark::State& state, const char* filename)
{
const std::string str = read_file(filename);
while (state.KeepRunning())
{
state.PauseTiming();
auto* d = new json_document();
state.ResumeTiming();
*d = json_document::parse(str);
state.PauseTiming();
delete d;
state.ResumeTiming();
}
state.SetBytesProcessed(state.iterations() * str.size());
}
JSON_VIEW_BENCHMARK_FILES(ViewParse);
//////////////////////////////////////////////////////////////////////////////
// parse into a document that is reused (its memory stays allocated)
//////////////////////////////////////////////////////////////////////////////
static void ViewRead(benchmark::State& state, const char* filename)
{
const std::string str = read_file(filename);
json_document d;
while (state.KeepRunning())
{
d.read(str);
benchmark::DoNotOptimize(d);
}
state.SetBytesProcessed(state.iterations() * str.size());
}
JSON_VIEW_BENCHMARK_FILES(ViewRead);
//////////////////////////////////////////////////////////////////////////////
// parse pretty-printed JSON (compare with ParseIndented)
//////////////////////////////////////////////////////////////////////////////
static void ViewParseIndented(benchmark::State& state, const char* filename, int indent)
{
const std::string indented = json::parse(read_file(filename)).dump(indent);
json_document d;
while (state.KeepRunning())
{
d.read(indented);
benchmark::DoNotOptimize(d);
}
state.SetBytesProcessed(state.iterations() * indented.size());
}
BENCHMARK_CAPTURE(ViewParseIndented, jeopardy / 4, TEST_DATA_DIRECTORY "/jeopardy/jeopardy.json", 4);
BENCHMARK_CAPTURE(ViewParseIndented, canada / 4, TEST_DATA_DIRECTORY "/nativejson-benchmark/canada.json", 4);
BENCHMARK_CAPTURE(ViewParseIndented, citm_catalog / 4, TEST_DATA_DIRECTORY "/nativejson-benchmark/citm_catalog.json", 4);
BENCHMARK_CAPTURE(ViewParseIndented, twitter / 4, TEST_DATA_DIRECTORY "/nativejson-benchmark/twitter.json", 4);
//////////////////////////////////////////////////////////////////////////////
// validate only (compare with Accept)
//////////////////////////////////////////////////////////////////////////////
static void ViewAccept(benchmark::State& state, const char* filename)
{
const std::string str = read_file(filename);
while (state.KeepRunning())
{
benchmark::DoNotOptimize(json_document::accept(str));
}
state.SetBytesProcessed(state.iterations() * str.size());
}
JSON_VIEW_BENCHMARK_FILES(ViewAccept);
//////////////////////////////////////////////////////////////////////////////
// convert a parsed document into a json value
//////////////////////////////////////////////////////////////////////////////
static void ViewMaterialize(benchmark::State& state, const char* filename)
{
const std::string str = read_file(filename);
const json_document d = json_document::parse(str);
while (state.KeepRunning())
{
state.PauseTiming();
auto* j = new json();
state.ResumeTiming();
*j = d.root().materialize();
state.PauseTiming();
delete j;
state.ResumeTiming();
}
state.SetBytesProcessed(state.iterations() * str.size());
}
JSON_VIEW_BENCHMARK_FILES(ViewMaterialize);