Conversation
pjfanning
force-pushed
the
actorpath-no-charat
branch
from
September 13, 2026 16:49
8effef2 to
29cc3ae
Compare
Motivation: ActorPath.findInvalidPathElementCharPosition runs for every named actorOf and scanned the name with String.charAt plus a pattern match with guards and a ValidSymbols.indexOf per non-alphanumeric character. String.charAt inlines to `isLatin1() ? StringLatin1.charAt : StringUTF16.charAt`, and that branch is profiled once, JVM-wide, in String.charAt's own bytecode. Any non-ASCII string handled anywhere in the process pollutes it, after which C2 compiles both coders into every charAt loop. Measured locally, the validator halves in throughput (101 -> 49 ops/us for "actor-1") once the profile is polluted. Modification: - Copy the element out once with getBytes(ISO_8859_1) (an intrinsic array copy for Latin-1 strings) and scan the byte[] with a 128-entry flag table (valid char / hex digit) instead of calling charAt per character. Characters outside Latin-1 encode as '?' and 0x80-0xFF as negative bytes, both invalid, so accepted set and reported position are unchanged. - Add directional ActorPathSpec tests for accepted names, rejected names and the reported position, including Latin-1, non-Latin-1 and surrogate-pair input. - Extend ActorPathValidationBenchmark with a `polluted` param that pre-warms String.charAt with UTF-16 strings, and keep the previous charAt-based validator there as charAtLoop* for comparison. Result: Name validation no longer depends on the String.charAt coder profile: 138-162 ops/us for "actor-1" polluted or not, versus 101 unpolluted and 49 polluted before. Also tried the same for MurmurHash.stringHash and Helpers.base64 and found no benefit (hash arithmetic and StringBuilder.append dominate), so those are unchanged. Tests: - sbt "actor-tests/testOnly org.apache.pekko.actor.ActorPathSpec org.apache.pekko.actor.LocalActorRefProviderSpec" (25 passed) - sbt "actor-tests/testOnly org.apache.pekko.routing.ConsistentHashingRouterSpec" (earlier run, passed) - sbt "actor/mimaReportBinaryIssues" (no issues) - sbt "bench-jmh/Jmh/compile" and Jmh/run of the pollution benchmark - scalafmt on changed Scala files, git diff --check References: None - performance follow-up to the hand-written actor name validator
pjfanning
force-pushed
the
actorpath-no-charat
branch
from
September 13, 2026 17:02
29cc3ae to
130f727
Compare
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
ActorPath.findInvalidPathElementCharPositionruns for every namedactorOf. It scanned the name withString.charAtinside a pattern match with guards, plus aValidSymbols.indexOffor every non-alphanumeric character.String.charAtinlines toisLatin1() ? StringLatin1.charAt : StringUTF16.charAt, and that branch is profiled once, JVM-wide, inString.charAt's own bytecode. Any non-ASCII string handled anywhere in the process (a Jackson payload, a log line) pollutes that profile, after which C2 compiles both coders into everycharAtloop. Measured locally, the validator halves in throughput once the profile is polluted.Modification
getBytes(ISO_8859_1)— an intrinsic array copy for Latin-1 strings — and scan thebyte[]with a 128-entry flag table (valid-char bit, hex-digit bit) instead of callingcharAtper character. Characters outside Latin-1 encode as?and 0x80–0xFF as negative bytes; both are invalid, so the accepted set and the reported position are unchanged.ActorPathSpectests for accepted names, rejected names and the reported error position, including Latin-1, non-Latin-1 and surrogate-pair input.ActorPathValidationBenchmarkwith apollutedparam that pre-warmsString.charAtwith UTF-16 strings, and keep the previouscharAt-based validator there ascharAtLoop*for comparison.Result
Name validation no longer depends on the
String.charAtcoder profile. Local JMH (-f 1, short run, noisy machine — the trend is what matters):charAtLoopActor_1(previous code)handLoopActor_1(this PR)charAtLoop7000(previous code)handLoop7000(this PR)I tried the same treatment on the other hot
charAtusers,MurmurHash.stringHashandHelpers.base64, and measured no benefit (hash arithmetic andStringBuilder.appenddominate;toCharArrayonly adds an allocation), so those are left as they are.Tests
sbt "actor-tests/testOnly org.apache.pekko.actor.ActorPathSpec org.apache.pekko.actor.LocalActorRefProviderSpec"— 25 passedsbt "actor-tests/testOnly org.apache.pekko.routing.ConsistentHashingRouterSpec"— passedsbt "actor/mimaReportBinaryIssues"— no issuessbt "bench-jmh/Jmh/compile"andJmh/runofActorPathValidationBenchmarkscalafmton changed Scala files,git diff --checkReferences
None - performance follow-up to the hand-written actor name validator