perf: allocation-free ASCII bitmask for std.stripChars (1.65x faster) (#851)#887
Open
He-Pin wants to merge 1 commit into
Open
perf: allocation-free ASCII bitmask for std.stripChars (1.65x faster) (#851)#887He-Pin wants to merge 1 commit into
He-Pin wants to merge 1 commit into
Conversation
…databricks#851) Motivation: std.stripChars/lstripChars/rstripChars built a `java.util.BitSet` per call for multi-character strip sets, allocating a BitSet object plus its backing `long[]` on every invocation and paying an array-load + bounds check per membership test. The vast majority of real strip sets are ASCII (whitespace/punctuation). Modification: - In StripUtils.strip, detect an all-ASCII strip set while scanning `chars` and build a 128-bit membership mask in two `long`s (no allocation). Strip via `stripAsciiMask`/`inAsciiMask`, which test membership with a shift+mask and no array access. Falls back to the existing BitSet path for BMP>127 sets and to the codepoint set for surrogates. Behavior is unchanged. - Add `StripBenchmark` (JMH) as a regression guard; relax `StripUtils` to `private[sjsonnet]` so the bench module can exercise it in isolation. Result: Isolated JMH micro (StripBenchmark, all-ASCII set, long leading/trailing runs, -f4, 48 samples): 2302.8 +/- 54.0 ns/op -> 1394.1 +/- 19.7 ns/op (1.65x faster), and gc.alloc.rate.norm 104 -> 48 B/op (the removed per-call BitSet; the remaining 48 B is just the result substring). Behavior verified identical to official jsonnet v0.22.0 across ASCII, multi-char, l/r, tab/newline, and non-ASCII fallback cases. Compiles on Scala 3.3.7 / 2.13.18 / 2.12.21; full JVM suite green. References: databricks#851
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation
std.stripChars/lstripChars/rstripCharsbuilt ajava.util.BitSetper call for multi-character strip sets — allocating a BitSet object plus its backinglong[]on every invocation, and paying an array-load + bounds check per membership test. The vast majority of real strip sets are ASCII (whitespace / punctuation).Modification
StripUtils.strip, detect an all-ASCII strip set while scanningcharsand build a 128-bit membership mask in twolongs (zero allocation). Strip viastripAsciiMask/inAsciiMask, testing membership with a shift + mask and no array access. Falls back to the existingBitSetpath for BMP>127 sets and to the codepoint set for surrogates. Behavior is unchanged.StripBenchmark(JMH) as a regression guard; relaxStripUtilstoprivate[sjsonnet]so the bench module can exercise it in isolation.Result
Isolated JMH micro (
StripBenchmark, all-ASCII set, long leading/trailing runs,-f4, 48 samples):1.65× faster, −54% allocation. The 56 B/op drop is exactly the removed per-call
BitSet(object +long[]); the remaining 48 B is just the resultsubstring.Behavior verified identical to official
jsonnet v0.22.0across ASCII, multi-char,l/r, tab/newline, and non-ASCII fallback cases. Compiles on Scala 3.3.7 / 2.13.18 / 2.12.21; full JVM test suite green.Addresses #851. (The residual gap vs jrsonnet noted in the issue is structural — UTF-16 vs byte-native strings — and out of scope here.)