Write the scalar elements of a container without entering dump_internal()

Every element of an array or object went through dump_internal(), whose
frame holds the state for descending into a container (callee-saved
registers, indentation, depth). A scalar, which is what most elements
are, needs none of it, and paid for the entry and exit regardless.

The loops now call dump_scalar() directly for anything that is not an
object or an array, and dump_internal() for those only.

Apple M1, Apple clang, retired instructions per dump(), on top of the
previous change:
  small object 2997 -> 2712 (-10 %)   signed_ints 276M -> 252M (-9 %)
  floats 323M -> 301M (-7 %)          jeopardy 501M -> 462M (-8 %)
  citm_catalog 5.96M -> 5.61M (-6 %)  twitter 4.40M -> 4.12M (-6 %)
  canada 39.2M -> 37.0M (-5 %)

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
Niels Lohmann committed 2026-10-22 09:28:13 +02:00
1 parent 5bbc7a1098
commit 7c3888fe42
2 files changed
+58 -16

No files matched your search

+29 -8
View File
@@ -178,7 +178,7 @@ class serializer
{
put_indent(new_indent);
put_quoted(i->first, "\": ");
dump_internal(i->second, new_indent, depth + 1);
dump_element(i->second, new_indent, depth + 1);
put_literal(",\n");
}
@@ -187,7 +187,7 @@ class serializer
JSON_ASSERT(std::next(i) == val.m_data.m_value.object->cend());
put_indent(new_indent);
put_quoted(i->first, "\": ");
dump_internal(i->second, new_indent, depth + 1);
dump_element(i->second, new_indent, depth + 1);
put_char('\n');
put_indent(current_indent);
@@ -202,7 +202,7 @@ class serializer
for (std::size_t cnt = 0; cnt < val.m_data.m_value.object->size() - 1; ++cnt, ++i)
{
put_quoted(i->first, "\":");
dump_internal(i->second, current_indent, depth + 1);
dump_element(i->second, current_indent, depth + 1);
put_char(',');
}
@@ -210,7 +210,7 @@ class serializer
JSON_ASSERT(i != val.m_data.m_value.object->cend());
JSON_ASSERT(std::next(i) == val.m_data.m_value.object->cend());
put_quoted(i->first, "\":");
dump_internal(i->second, current_indent, depth + 1);
dump_element(i->second, current_indent, depth + 1);
put_char('}');
}
@@ -244,14 +244,14 @@ class serializer
i != val.m_data.m_value.array->cend() - 1; ++i)
{
put_indent(new_indent);
dump_internal(*i, new_indent, depth + 1);
dump_element(*i, new_indent, depth + 1);
put_literal(",\n");
}
// last element
JSON_ASSERT(!val.m_data.m_value.array->empty());
put_indent(new_indent);
dump_internal(val.m_data.m_value.array->back(), new_indent, depth + 1);
dump_element(val.m_data.m_value.array->back(), new_indent, depth + 1);
put_char('\n');
put_indent(current_indent);
@@ -265,13 +265,13 @@ class serializer
for (auto i = val.m_data.m_value.array->cbegin();
i != val.m_data.m_value.array->cend() - 1; ++i)
{
dump_internal(*i, current_indent, depth + 1);
dump_element(*i, current_indent, depth + 1);
put_char(',');
}
// last element
JSON_ASSERT(!val.m_data.m_value.array->empty());
dump_internal(val.m_data.m_value.array->back(), current_indent, depth + 1);
dump_element(val.m_data.m_value.array->back(), current_indent, depth + 1);
put_char(']');
}
@@ -294,6 +294,27 @@ class serializer
}
private:
/*!
@brief serialize an element of a container
Most elements are scalars, which need none of the state dump_internal() keeps
for a container; writing them from here spares each the entry and exit of
that function.
*/
JSON_HEDLEY_ALWAYS_INLINE void dump_element(const BasicJsonType& val,
const std::size_t current_indent,
const std::size_t depth)
{
if (val.m_data.m_type == value_t::object || val.m_data.m_type == value_t::array)
{
dump_internal(val, current_indent, depth);
}
else
{
dump_scalar(val, current_indent);
}
}
/*!
@brief write out @a val and everything below it without the call stack
+29 -8
View File
@@ -30322,7 +30322,7 @@ class serializer
{
put_indent(new_indent);
put_quoted(i->first, "\": ");
dump_internal(i->second, new_indent, depth + 1);
dump_element(i->second, new_indent, depth + 1);
put_literal(",\n");
}
@@ -30331,7 +30331,7 @@ class serializer
JSON_ASSERT(std::next(i) == val.m_data.m_value.object->cend());
put_indent(new_indent);
put_quoted(i->first, "\": ");
dump_internal(i->second, new_indent, depth + 1);
dump_element(i->second, new_indent, depth + 1);
put_char('\n');
put_indent(current_indent);
@@ -30346,7 +30346,7 @@ class serializer
for (std::size_t cnt = 0; cnt < val.m_data.m_value.object->size() - 1; ++cnt, ++i)
{
put_quoted(i->first, "\":");
dump_internal(i->second, current_indent, depth + 1);
dump_element(i->second, current_indent, depth + 1);
put_char(',');
}
@@ -30354,7 +30354,7 @@ class serializer
JSON_ASSERT(i != val.m_data.m_value.object->cend());
JSON_ASSERT(std::next(i) == val.m_data.m_value.object->cend());
put_quoted(i->first, "\":");
dump_internal(i->second, current_indent, depth + 1);
dump_element(i->second, current_indent, depth + 1);
put_char('}');
}
@@ -30388,14 +30388,14 @@ class serializer
i != val.m_data.m_value.array->cend() - 1; ++i)
{
put_indent(new_indent);
dump_internal(*i, new_indent, depth + 1);
dump_element(*i, new_indent, depth + 1);
put_literal(",\n");
}
// last element
JSON_ASSERT(!val.m_data.m_value.array->empty());
put_indent(new_indent);
dump_internal(val.m_data.m_value.array->back(), new_indent, depth + 1);
dump_element(val.m_data.m_value.array->back(), new_indent, depth + 1);
put_char('\n');
put_indent(current_indent);
@@ -30409,13 +30409,13 @@ class serializer
for (auto i = val.m_data.m_value.array->cbegin();
i != val.m_data.m_value.array->cend() - 1; ++i)
{
dump_internal(*i, current_indent, depth + 1);
dump_element(*i, current_indent, depth + 1);
put_char(',');
}
// last element
JSON_ASSERT(!val.m_data.m_value.array->empty());
dump_internal(val.m_data.m_value.array->back(), current_indent, depth + 1);
dump_element(val.m_data.m_value.array->back(), current_indent, depth + 1);
put_char(']');
}
@@ -30438,6 +30438,27 @@ class serializer
}
private:
/*!
@brief serialize an element of a container
Most elements are scalars, which need none of the state dump_internal() keeps
for a container; writing them from here spares each the entry and exit of
that function.
*/
JSON_HEDLEY_ALWAYS_INLINE void dump_element(const BasicJsonType& val,
const std::size_t current_indent,
const std::size_t depth)
{
if (val.m_data.m_type == value_t::object || val.m_data.m_type == value_t::array)
{
dump_internal(val, current_indent, depth);
}
else
{
dump_scalar(val, current_indent);
}
}
/*!
@brief write out @a val and everything below it without the call stack