WIP: template append_raw on size for compile-time-inlined memcpy

Add append_raw_n<N>(const char*) which propagates the key length as a
template parameter, letting the compiler emit a fully inlined memcpy
with a constant size (direct loads/stores) instead of the size-passed-
as-runtime-arg variant which sometimes fell back to a libc memcpy
dispatch.

Use it in the reflection struct atom for both first_key and rest_key
(both are compile-time constants from define_static_string).

Measured (TRUE A/B, 7 alternating rounds in single docker invocation,
two independent runs combined):
  CITM:    baseline ~4395 -> patched ~4791 MB/s  (+9%)
           Glaze    ~4810 MB/s -> simdjson now at PARITY with Glaze
                                  (within +/- 3% noise across runs)
  Twitter: still well ahead of Glaze, no change from baseline

Output is byte-identical, all reflection comprehensive tests pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Francisco Geiman Thiesen
2026-05-05 15:45:08 -07:00
parent 0a694273e2
commit 0ab7e2d701
3 changed files with 21 additions and 4 deletions
@@ -100,12 +100,12 @@ simdjson_really_inline constexpr void atom(string_builder &b, const T &t) {
constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(dm)) + ":");
constexpr auto rest_key = std::define_static_string(
std::string(",") + constevalutil::consteval_to_quoted_escaped(std::meta::identifier_of(dm)) + ":");
// Pass size explicitly so we avoid the runtime strlen() in
// append_raw(const char*) — these keys are compile-time constants.
// Pass size as template parameter so memcpy is fully inlined with
// a compile-time-constant size.
constexpr size_t first_key_len = std::char_traits<char>::length(first_key);
constexpr size_t rest_key_len = std::char_traits<char>::length(rest_key);
if (i == 0) b.append_raw(first_key, first_key_len);
else b.append_raw(rest_key, rest_key_len);
if (i == 0) b.template append_raw_n<first_key_len>(first_key);
else b.template append_raw_n<rest_key_len>(rest_key);
atom(b, t.[:dm:]);
i++;
};
@@ -780,6 +780,14 @@ simdjson_inline void string_builder::append_raw(const char *str,
position += len;
}
}
template <size_t N>
simdjson_inline void string_builder::append_raw_n(const char *str) noexcept {
if (capacity_check(N)) {
std::memcpy(buffer.get() + position, str, N);
position += N;
}
}
#if SIMDJSON_SUPPORTS_CONCEPTS
// Support for optional types (std::optional, etc.)
template <concepts::optional_type T>
@@ -185,6 +185,15 @@ requires (!std::is_convertible<R, std::string_view>::value && !concepts::optiona
* There is no UTF-8 validation.
*/
simdjson_inline void append_raw(const char *str, size_t len) noexcept;
/**
* Append exactly N characters from str. The length is a template parameter
* so the compiler can fully inline the memcpy with a compile-time-constant
* size, avoiding the libc call. Used for compile-time-constant keys in the
* reflection struct atom.
*/
template <size_t N>
simdjson_inline void append_raw_n(const char *str) noexcept;
#if SIMDJSON_EXCEPTIONS
/**
* Creates an std::string from the written JSON buffer.