From 0ab7e2d701b160cbf0b92c89caf7900a92194162 Mon Sep 17 00:00:00 2001 From: Francisco Geiman Thiesen Date: Tue, 5 May 2026 15:45:08 -0700 Subject: [PATCH] WIP: template append_raw on size for compile-time-inlined memcpy Add append_raw_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) --- include/simdjson/generic/builder/json_builder.h | 8 ++++---- .../simdjson/generic/builder/json_string_builder-inl.h | 8 ++++++++ include/simdjson/generic/builder/json_string_builder.h | 9 +++++++++ 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/include/simdjson/generic/builder/json_builder.h b/include/simdjson/generic/builder/json_builder.h index c0e2c72cc..4b4a4a9ea 100644 --- a/include/simdjson/generic/builder/json_builder.h +++ b/include/simdjson/generic/builder/json_builder.h @@ -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::length(first_key); constexpr size_t rest_key_len = std::char_traits::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); + else b.template append_raw_n(rest_key); atom(b, t.[:dm:]); i++; }; diff --git a/include/simdjson/generic/builder/json_string_builder-inl.h b/include/simdjson/generic/builder/json_string_builder-inl.h index 46ded99ad..7860cfaca 100644 --- a/include/simdjson/generic/builder/json_string_builder-inl.h +++ b/include/simdjson/generic/builder/json_string_builder-inl.h @@ -780,6 +780,14 @@ simdjson_inline void string_builder::append_raw(const char *str, position += len; } } + +template +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 diff --git a/include/simdjson/generic/builder/json_string_builder.h b/include/simdjson/generic/builder/json_string_builder.h index 39196d182..187a76a16 100644 --- a/include/simdjson/generic/builder/json_string_builder.h +++ b/include/simdjson/generic/builder/json_string_builder.h @@ -185,6 +185,15 @@ requires (!std::is_convertible::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 + simdjson_inline void append_raw_n(const char *str) noexcept; #if SIMDJSON_EXCEPTIONS /** * Creates an std::string from the written JSON buffer.