From d05821fd0018c6fcb0121f4b44dd36451b0307a1 Mon Sep 17 00:00:00 2001 From: He-Pin Date: Sun, 24 May 2026 00:01:14 +0800 Subject: [PATCH] perf: buffer json file output Motivation: The Native stdout buffering win showed downstream buffering can materially reduce large-output write overhead. JSON outputFile rendering still wrote ByteRenderer chunks directly to the file output stream. Modification: Wrap the JSON outputFile ByteRenderer target in a BufferedOutputStream with a 256 KiB buffer. Keep YAML, expect-string, stdout, and renderer semantics unchanged. Result: Native kube-prometheus file-output A/B on the 0.5.12 stack improved in both orders: forward median 196.625 ms clean vs 183.491 ms candidate; reverse median 175.878 ms candidate vs 193.394 ms clean. Output matched by cmp; reformat, full tests, and bench regressions passed. --- sjsonnet/src-jvm-native/sjsonnet/SjsonnetMainBase.scala | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sjsonnet/src-jvm-native/sjsonnet/SjsonnetMainBase.scala b/sjsonnet/src-jvm-native/sjsonnet/SjsonnetMainBase.scala index ad31d4c5..7b8e9f17 100644 --- a/sjsonnet/src-jvm-native/sjsonnet/SjsonnetMainBase.scala +++ b/sjsonnet/src-jvm-native/sjsonnet/SjsonnetMainBase.scala @@ -22,6 +22,7 @@ object SjsonnetMainBase { * Sentinel value returned when output was already written directly to stdout via byte pipeline. */ private val ByteRenderedSentinel = "\u0000" + private final val OutputBufferSize = 256 * 1024 class SimpleImporter( searchRoots0: Seq[Path], // Evaluated in order, first occurrence wins @@ -315,14 +316,14 @@ object SjsonnetMainBase { config.outputFile match { case Some(f) if !config.yamlOut.value && !config.expectString.value => // Byte[] fast path: render directly to OutputStream, bypassing OutputStreamWriter. - // ByteBuilder handles buffering internally (8KB threshold), no BufferedOutputStream needed. handleWriteFile( os.write.over.outputStream(os.Path(f, wd), createFolders = config.createDirs.value) ).flatMap { out => try { - val renderer = new ByteRenderer(out, indent = config.indent) + val buf = new BufferedOutputStream(out, SjsonnetMainBase.OutputBufferSize) + val renderer = new ByteRenderer(buf, indent = config.indent) val res = interp.interpret0(jsonnetCode, OsPath(path), renderer) - out.flush() + buf.flush() res.map(_ => "") } finally out.close() }