You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Tracking issue for a specific perf gap found while comparing sjsonnet (native, master) against jrsonnet (master). Parent comparison: #666. Biggest single gap in the comparison — worth prioritizing.
Observation
Large string template (% format operator on a multi-KB text block) is 2.90× slower than jrsonnet.
Scenario: bench/resources/cpp_suite/large_string_template.jsonnet — applies |||...||| % { x: 3 } on a ~7.8k-line text block of mostly ASCII.
sjsonnet/src/sjsonnet/Format.scala — % operator builds the formatted string char-by-char into a StringBuilder.
sjsonnet/src/sjsonnet/BaseByteRenderer.scala:309-348 — visitLongString renders the final string into JSON. Calls str.getBytes(UTF_8), runs SWAR findFirstEscapeChar, then copies chunks between escapes.
Since x has only one occurrence and the template contains mostly literal text with sparse \n, the format engine is essentially a giant memcpy — jrsonnet manages this with roughly zero copies.
Hypothesis
Double conversion: jsonnet string is UTF-16 String. Format.scala builds into StringBuilder (UTF-16). Then JSON render does str.getBytes(UTF_8) — a full UTF-8 encode pass. That's the conversion cost base64 encode/decode is ~6x slower than jrsonnet on large payloads #779 describes, paid once on an ~N KB output.
Format engine scans every character even when there are no format specifiers in a long literal run.
Large string literal parse/alloc: the |||...||| block is a ~600 KB literal. Parser allocates it once, but if the format engine then concatenates the unchanged literal text into a new StringBuilder, that's an extra allocation.
Directions
Short-term: In Format.scala, detect long literal runs between format specifiers and use StringBuilder.append(String, start, end) (which avoids per-char virtual dispatch) or bulk arraycopy.
Medium-term: When Val.Str is asciiSafe (tracked via Val.Str.asciiSafe), skip the getBytes(UTF_8) in BaseByteRenderer.visitLongString and reuse the char-to-byte fast path already used by renderAsciiSafeString. This is the single biggest lever against the real-world kube-prometheus gap (which also emits large manifests of mostly-ASCII strings).
Tracking issue for a specific perf gap found while comparing sjsonnet (native, master) against jrsonnet (master). Parent comparison: #666. Biggest single gap in the comparison — worth prioritizing.
Observation
Large string template (
%format operator on a multi-KB text block) is 2.90× slower than jrsonnet.Scenario:
bench/resources/cpp_suite/large_string_template.jsonnet— applies|||...||| % { x: 3 }on a ~7.8k-line text block of mostly ASCII.Repro:
Code
Two hot paths:
sjsonnet/src/sjsonnet/Format.scala—%operator builds the formatted string char-by-char into aStringBuilder.sjsonnet/src/sjsonnet/BaseByteRenderer.scala:309-348—visitLongStringrenders the final string into JSON. Callsstr.getBytes(UTF_8), runs SWARfindFirstEscapeChar, then copies chunks between escapes.Since
xhas only one occurrence and the template contains mostly literal text with sparse\n, the format engine is essentially a giant memcpy — jrsonnet manages this with roughly zero copies.Hypothesis
String.Format.scalabuilds intoStringBuilder(UTF-16). Then JSON render doesstr.getBytes(UTF_8)— a full UTF-8 encode pass. That's the conversion cost base64 encode/decode is ~6x slower than jrsonnet on large payloads #779 describes, paid once on an ~N KB output.|||...|||block is a ~600 KB literal. Parser allocates it once, but if the format engine then concatenates the unchanged literal text into a newStringBuilder, that's an extra allocation.Directions
Format.scala, detect long literal runs between format specifiers and useStringBuilder.append(String, start, end)(which avoids per-char virtual dispatch) or bulkarraycopy.Val.StrisasciiSafe(tracked viaVal.Str.asciiSafe), skip thegetBytes(UTF_8)inBaseByteRenderer.visitLongStringand reuse the char-to-byte fast path already used byrenderAsciiSafeString. This is the single biggest lever against the real-world kube-prometheus gap (which also emits large manifests of mostly-ASCII strings).Val.Strvariant for pre-decoded strings read from disk or already known to be ASCII/UTF-8 bytes — avoids the UTF-16 round-trip entirely. Overlaps with base64 encode/decode is ~6x slower than jrsonnet on large payloads #779.Part of the jrsonnet-parity effort tracked in #666.