ByteString: extract shared private helpers to reduce code duplication#2859
ByteString: extract shared private helpers to reduce code duplication#2859pjfanning wants to merge 5 commits intoapache:mainfrom
Conversation
… reuse - Add `unrolledFirstIndexOf(bytes, fromIndex, byteCount, value)` and `unrolledLastIndexOf(bytes, fromIndex, byteCount, value)` shared helpers to `object ByteString`, replacing identical private methods that were duplicated in `ByteString1C` and `ByteString1`. - Add `swarFirstIndexOf(bytes, fromOffset, searchLength, elem)` helper that contains the SWAR search loop, eliminating duplication between `indexOf(Byte, Int)` and `indexOf(Byte, Int, Int)` in both `ByteString1C` and `ByteString1`. - Add `swarLastIndexOf(bytes, baseOffset, searchLength, elem)` helper that contains the reverse SWAR search loop, eliminating duplication between the `lastIndexOf(Byte, Int)` implementations in `ByteString1C` and `ByteString1`. - Make `indexOf[B >: Byte]` in `ByteString1C`, `ByteString1`, and `ByteStrings` dispatch to the Byte-specific SWAR overload for Byte inputs (pattern match on elem), eliminating the near-identical `find` inner function that was duplicated between `indexOf[B]` and `indexOf(Byte, Int)` in `ByteStrings`, and improving performance for Byte searches in all three classes. - Make `lastIndexOf[B >: Byte]` in `ByteStrings` likewise dispatch to `lastIndexOf(Byte, Int)` for Byte inputs, removing the duplicate `find` function. - Net result: -89 lines of code with no behaviour change; all public APIs unchanged; HotSpot-friendly (small, static, easily-inlineable helpers). Agent-Logs-Url: https://github.com/pjfanning/incubator-pekko/sessions/3ba37c44-b65b-4094-9f11-7541417d7c37 Co-authored-by: pjfanning <11783444+pjfanning@users.noreply.github.com>
Agent-Logs-Url: https://github.com/pjfanning/incubator-pekko/sessions/3ba37c44-b65b-4094-9f11-7541417d7c37 Co-authored-by: pjfanning <11783444+pjfanning@users.noreply.github.com>
…l helper in ByteString1 Agent-Logs-Url: https://github.com/pjfanning/incubator-pekko/sessions/3ba37c44-b65b-4094-9f11-7541417d7c37 Co-authored-by: pjfanning <11783444+pjfanning@users.noreply.github.com>
…consistency Agent-Logs-Url: https://github.com/pjfanning/incubator-pekko/sessions/3ba37c44-b65b-4094-9f11-7541417d7c37 Co-authored-by: pjfanning <11783444+pjfanning@users.noreply.github.com>
|
My computer is getting a bit old and is not great at providing consistent conditions during JMH runs. |
|
I can verify it later |
| @@ -1141,6 +975,92 @@ object ByteString { | |||
| def SerializationIdentity: Byte | |||
There was a problem hiding this comment.
💡 Consider @inline for swarFirstIndexOf / swarLastIndexOf / absToLogical
These helpers are on the hot path for every indexOf/lastIndexOf call. After extraction from in-class methods to companion-object private methods, the JVM JIT may still inline them eventually, but:
absToLogicalis tiny (if (absIdx == -1) -1 else absIdx - startIndex) — a perfect candidate for@inlineto avoid the call overhead in tight loopsswarFirstIndexOfandswarLastIndexOfare called from three differentByteStringsubclasses — without@inline, each call site adds a frame
The original code had these methods inline within each class (effectively monomorphic), so the JIT had an easier time. Extracting to shared helpers introduces polymorphic call sites.
Suggestion:
@inline private def absToLogical(absIdx: Int): Int = ...
@inline private def swarFirstIndexOf(...): Int = ...
@inline private def swarLastIndexOf(...): Int = ...This ensures zero overhead from the deduplication — best of both worlds.
|
ByteString1CandByteString1contained near-identical SWAR search loops and unrolled byte-scan helpers duplicated verbatim across both classes.ByteStringssimilarly had identicalfindinner functions in its generic andByte-typedindexOf/lastIndexOfoverloads.New shared helpers in
object ByteStringunrolledFirstIndexOf(bytes, fromIndex, byteCount, value)/unrolledLastIndexOf(...)— unrolled 1–7 byte scans, previously duplicated in bothByteString1CandByteString1swarFirstIndexOf(bytes, fromOffset, searchLength, elem)— SWAR forward-search loop; eliminates duplication betweenindexOf(Byte, Int)andindexOf(Byte, Int, Int)in both classes; returns absolute indexswarLastIndexOf(bytes, baseOffset, searchLength, elem)— SWAR reverse-search loop; same rationale; returns absolute index (consistent withswarFirstIndexOf)Per-class changes
ByteString1gains a privateabsToLogical(absIdx)helper to eliminate the repeatedif (absResult == -1) -1 else absResult - startIndexpattern in all four index-search methodsByteString1C/ByteString1/ByteStrings:indexOf[B >: Byte]now pattern-matches onelemand dispatches to the Byte-specific SWAR overload forByteinputs — removing the duplicatefindinner function inByteStringsand improving performance forByteinputs (avoids boxing)ByteStrings:lastIndexOf[B >: Byte]follows the same dispatch patternAll public APIs are unchanged. Net: −80 lines. All helpers are small and static, keeping HotSpot inlining characteristics equivalent to the original inlined code.