CBOR/MessagePack readers: leave one-element arrays to the general path

enter_cbor_array and enter_msgpack_array called the out-of-line scalar
run for every non-empty array. When the array's element is an object or
an array, as on every level of [[...]] or the single ring of a GeoJSON
polygon, the call reads nothing; it made 1,000 arrays nested in each
other cost 4 % more instructions (GCC 13) and up to 8 % more time (Apple
clang) than without the run. #5975 had fixed that with an inline
pre-check, which was later dropped after measuring canada only.

The run is now entered only for arrays of two or more elements: the same
comparison as before, so no other row changes. Arrays of one scalar,
which the run read faster, now take the general path; they are rare in
documents (one in citm_catalog, none in twitter or canada). The new
benchmark FromBinaryShape/singles measures them, next to
FromBinaryShape/nested.

Instructions per repetition, new/old, GCC 13 callgrind: nested arrays
0.973 (CBOR) and 0.971 (MessagePack), arrays of one scalar 1.037 and
1.021, every other row within 0.15 %.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
Niels Lohmann committed 2026-11-02 08:22:32 +01:00
1 parent b2674a4a2f
commit 16ed40b2d4
5 files changed
+96 -6

No files matched your search

+18
View File
@@ -629,6 +629,22 @@ static json make_containers()
return j;
}
/// many arrays of one number each: one-element arrays are left to the general
/// path (CBOR and MessagePack), which reads a lone number slower than the loop
/// for arrays of scalars would
static json make_singles()
{
json data = json::array();
for (std::size_t i = 0; i < 100000; ++i)
{
data.push_back(json::array({i}));
}
json j = json::object();
j["data"] = std::move(data);
return j;
}
/// one flat array of numbers: the scalar decoding path, which must not move
static json make_scalars()
{
@@ -676,6 +692,8 @@ BENCHMARK_CAPTURE(FromBinaryShape, containers / ubjson_optimized, make_container
BENCHMARK_CAPTURE(FromBinaryShape, containers / bjdata, make_containers, binary_format::bjdata);
BENCHMARK_CAPTURE(FromBinaryShape, containers / bson, make_containers, binary_format::bson);
BENCHMARK_CAPTURE(FromBinaryShape, containers / bon8, make_containers, binary_format::bon8);
BENCHMARK_CAPTURE(FromBinaryShape, singles / cbor, make_singles, binary_format::cbor);
BENCHMARK_CAPTURE(FromBinaryShape, singles / msgpack, make_singles, binary_format::msgpack);
// BSON names every array element, so a large array measures key generation
// rather than scalar decoding and is left out here
BENCHMARK_CAPTURE(FromBinaryShape, scalars / cbor, make_scalars, binary_format::cbor);