Skip to content

Use Java 17 string and hex fast paths#810

Open
He-Pin wants to merge 1 commit intodatabricks:masterfrom
He-Pin:perf/jdk17-platform-optimizations
Open

Use Java 17 string and hex fast paths#810
He-Pin wants to merge 1 commit intodatabricks:masterfrom
He-Pin:perf/jdk17-platform-optimizations

Conversation

@He-Pin
Copy link
Copy Markdown
Contributor

@He-Pin He-Pin commented Apr 30, 2026

Summary

This PR uses the Java 17 baseline to move a few hot string/encoding paths onto JIT/GC-friendlier implementations while keeping Scala.js and Scala Native behavior covered by platform fallbacks.

  • JVM uses String.repeat for repeated string padding / std.repeat and HexFormat for digest-to-hex.
  • JS/Native keep Platform.repeatString fallbacks; Native also gets a manual digest-to-hex loop instead of per-byte String.format.
  • UTF-8 conversions in touched native helpers now use StandardCharsets.UTF_8 instead of charset names / default charset.
  • std.splitLimit advances with String.indexOf, std.resolvePath uses lastIndexOf, and CLI binding parsing avoids regex String.split.
  • Adds small reproducible JMH inputs under bench/resources/jdk17_suite as supplemental targeted coverage.
  • Adds compact directional tests for repeat/format/split/resolvePath and one JVM/Native Unicode hash vector.

I intentionally did not use Files.readString: its malformed-input behavior is not equivalent to the current new String(Files.readAllBytes(...), UTF_8) decode path. I also left stream/matcher helpers and String.strip/isBlank/lines/formatted out of scope because they either change Jsonnet semantics, add allocation-heavy pipelines, or do not map to the current hot paths.

This is not claimed as a broad existing-suite speedup or as the theoretical limit of all Java 17 APIs. The existing JMH suite is mostly neutral/noisy; the measurable wins are concentrated in the string repeat/split/resolve/hash paths shown below.

JMH

Environment:

  • JVM: Zulu OpenJDK 21.0.10 on macOS/aarch64
  • Project baseline: current master (3a9a4928, Java 17 compile level)
  • Existing-suite flags: -wi 3 -i 5 -w 1s -r 2s -f 1 -jvmArgsAppend -Xss100m -prof gc
  • Targeted-suite flags: -wi 3 -i 5 -w 1s -r 2s -f 1 -prof gc

Existing JMH Suite

This run uses the 36 existing bench/resources/{bug_suite,cpp_suite,go_suite,sjsonnet_suite} inputs and excludes the new jdk17_suite files.

metric result
time geomean +1.21%
allocation geomean -0.10%
time count, <= -1% / flat / >= +1% 10 / 10 / 16
allocation count, <= -1% / flat / >= +1% 1 / 35 / 0

Top existing-suite timing movements:

direction benchmark base ms/op opt ms/op time alloc
faster bench/resources/cpp_suite/bench.07.jsonnet 3.124 2.573 -17.6% -0.01%
faster bench/resources/cpp_suite/bench.01.jsonnet 0.047 0.045 -3.7% -0.05%
faster bench/resources/go_suite/manifestYamlDoc.jsonnet 0.056 0.055 -2.6% -0.04%
faster bench/resources/go_suite/manifestTomlEx.jsonnet 0.069 0.068 -2.0% +0.40%
faster bench/resources/go_suite/lstripChars.jsonnet 0.115 0.113 -1.7% -0.00%
slower bench/resources/cpp_suite/realistic2.jsonnet 39.855 47.023 +18.0% +0.01%
slower bench/resources/cpp_suite/bench.02.jsonnet 25.955 29.907 +15.2% +0.00%
slower bench/resources/sjsonnet_suite/setInter.jsonnet 0.348 0.379 +9.0% +0.02%
slower bench/resources/cpp_suite/large_string_join.jsonnet 0.539 0.583 +8.2% -0.00%
slower bench/resources/go_suite/substr.jsonnet 0.055 0.058 +5.4% -0.00%

Most allocation-positive existing-suite movements:

benchmark alloc time
bench/resources/cpp_suite/bench.09.jsonnet -1.57% -0.2%
bench/resources/go_suite/escapeStringJson.jsonnet -0.97% -1.6%
bench/resources/bug_suite/assertions.jsonnet -0.79% +3.5%
bench/resources/go_suite/base64_stress.jsonnet -0.64% +1.2%
bench/resources/go_suite/comparison.jsonnet -0.09% +0.3%

Interpretation: allocation is effectively flat on the existing suite, with no >= +1% allocation regressions. Timing is mixed and slightly slower on geomean in this single run; the largest slower entries have essentially unchanged allocation, so I am treating the existing-suite result as neutral/noisy rather than a throughput win. I am therefore not claiming an existing-suite throughput improvement from this PR.

Full existing-suite table
benchmark base ms/op opt ms/op time base B/op opt B/op alloc
bench/resources/bug_suite/assertions.jsonnet 0.191 0.198 +3.5% 644,769 639,692 -0.79%
bench/resources/cpp_suite/bench.01.jsonnet 0.047 0.045 -3.7% 80,329 80,289 -0.05%
bench/resources/cpp_suite/bench.02.jsonnet 25.955 29.907 +15.2% 81,680,412 81,681,024 +0.00%
bench/resources/cpp_suite/bench.03.jsonnet 6.722 6.782 +0.9% 5,917,382 5,917,344 -0.00%
bench/resources/cpp_suite/bench.04.jsonnet 0.110 0.109 -1.3% 633,739 633,795 +0.01%
bench/resources/cpp_suite/bench.06.jsonnet 0.217 0.227 +4.5% 878,935 878,969 +0.00%
bench/resources/cpp_suite/bench.07.jsonnet 3.124 2.573 -17.6% 2,970,731 2,970,445 -0.01%
bench/resources/cpp_suite/bench.08.jsonnet 0.039 0.038 -1.4% 81,945 81,953 +0.01%
bench/resources/cpp_suite/bench.09.jsonnet 0.042 0.042 -0.2% 91,217 89,785 -1.57%
bench/resources/cpp_suite/gen_big_object.jsonnet 0.806 0.829 +2.8% 4,015,687 4,015,736 +0.00%
bench/resources/cpp_suite/large_string_join.jsonnet 0.539 0.583 +8.2% 1,530,958 1,530,940 -0.00%
bench/resources/cpp_suite/large_string_template.jsonnet 1.579 1.559 -1.3% 7,776,578 7,776,501 -0.00%
bench/resources/cpp_suite/realistic1.jsonnet 1.415 1.412 -0.3% 5,875,529 5,875,726 +0.00%
bench/resources/cpp_suite/realistic2.jsonnet 39.855 47.023 +18.0% 75,875,290 75,880,789 +0.01%
bench/resources/go_suite/base64.jsonnet 0.145 0.150 +3.7% 2,184,109 2,184,045 -0.00%
bench/resources/go_suite/base64Decode.jsonnet 0.116 0.117 +0.5% 1,547,577 1,547,529 -0.00%
bench/resources/go_suite/base64DecodeBytes.jsonnet 5.017 5.217 +4.0% 232,418 232,580 +0.07%
bench/resources/go_suite/base64_byte_array.jsonnet 0.772 0.782 +1.3% 4,694,249 4,694,403 +0.00%
bench/resources/go_suite/base64_stress.jsonnet 0.175 0.178 +1.2% 842,575 837,150 -0.64%
bench/resources/go_suite/comparison.jsonnet 0.029 0.029 +0.3% 60,617 60,561 -0.09%
bench/resources/go_suite/comparison2.jsonnet 17.200 17.209 +0.1% 37,670,053 37,670,139 +0.00%
bench/resources/go_suite/escapeStringJson.jsonnet 0.032 0.031 -1.6% 77,336 76,584 -0.97%
bench/resources/go_suite/foldl.jsonnet 0.071 0.070 -0.9% 350,674 350,602 -0.02%
bench/resources/go_suite/lstripChars.jsonnet 0.115 0.113 -1.7% 1,533,089 1,533,057 -0.00%
bench/resources/go_suite/manifestJsonEx.jsonnet 0.052 0.051 -0.7% 132,273 132,185 -0.07%
bench/resources/go_suite/manifestTomlEx.jsonnet 0.069 0.068 -2.0% 145,153 145,737 +0.40%
bench/resources/go_suite/manifestYamlDoc.jsonnet 0.056 0.055 -2.6% 135,977 135,921 -0.04%
bench/resources/go_suite/member.jsonnet 0.635 0.630 -0.7% 2,594,716 2,594,889 +0.01%
bench/resources/go_suite/parseInt.jsonnet 0.032 0.032 +0.1% 68,009 68,113 +0.15%
bench/resources/go_suite/reverse.jsonnet 6.765 6.653 -1.7% 11,836,940 11,837,065 +0.00%
bench/resources/go_suite/rstripChars.jsonnet 0.113 0.115 +1.9% 1,532,424 1,532,425 +0.00%
bench/resources/go_suite/stripChars.jsonnet 0.112 0.114 +1.5% 1,523,152 1,523,048 -0.01%
bench/resources/go_suite/substr.jsonnet 0.055 0.058 +5.4% 507,139 507,115 -0.00%
bench/resources/sjsonnet_suite/setDiff.jsonnet 0.386 0.398 +3.1% 1,328,027 1,328,178 +0.01%
bench/resources/sjsonnet_suite/setInter.jsonnet 0.348 0.379 +9.0% 1,266,231 1,266,465 +0.02%
bench/resources/sjsonnet_suite/setUnion.jsonnet 0.582 0.589 +1.3% 1,509,564 1,509,463 -0.01%

Command:

PATHS=$(find bench/resources/bug_suite bench/resources/cpp_suite bench/resources/go_suite bench/resources/sjsonnet_suite -type f -name '*.jsonnet' | sort | paste -sd, -)
./mill --no-server bench.runJmh sjsonnet.bench.RegressionBenchmark.main \
  -p path="$PATHS" \
  -wi 3 -i 5 -w 1s -r 2s -f 1 \
  -jvmArgsAppend -Xss100m \
  -prof gc \
  -rf json -rff /private/tmp/sjsonnet-jdk17-bench/{base,opt}-existing3.json

Supplemental Targeted JMH

These new inputs isolate the paths changed in this PR; they are supplemental and are not a replacement for the existing-suite comparison above.

benchmark base ms/op opt ms/op time base B/op opt B/op alloc
bench/resources/jdk17_suite/hash.jsonnet 1.584 1.611 +1.7% 4,222,936 3,801,302 -9.98%
bench/resources/jdk17_suite/repeat_format.jsonnet 0.270 0.235 -13.1% 741,375 724,943 -2.22%
bench/resources/jdk17_suite/split_resolve.jsonnet 0.203 0.141 -30.3% 846,347 690,568 -18.41%

Targeted geomean:

  • time: -14.93%
  • allocation: -10.45%

Command:

./mill --no-server bench.runJmh sjsonnet.bench.RegressionBenchmark.main \
  -p path=bench/resources/jdk17_suite/hash.jsonnet,bench/resources/jdk17_suite/repeat_format.jsonnet,bench/resources/jdk17_suite/split_resolve.jsonnet \
  -wi 3 -i 5 -w 1s -r 2s -f 1 \
  -prof gc \
  -rf json -rff /private/tmp/sjsonnet-jdk17-bench/{base,opt}-targeted2.json

Correctness / Tests

./mill --no-server 'sjsonnet.jvm[3.3.7].checkFormat'
./mill --no-server 'sjsonnet.jvm[3.3.7].test'
./mill --no-server 'sjsonnet.js[3.3.7].test'
./mill --no-server 'sjsonnet.native[3.3.7].test'

All passed locally.

@He-Pin He-Pin marked this pull request as draft April 30, 2026 07:34
@He-Pin He-Pin marked this pull request as ready for review April 30, 2026 07:53
@stephenamar-db stephenamar-db self-requested a review May 1, 2026 17:44
@stephenamar-db
Copy link
Copy Markdown
Collaborator

rebase

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants