This is an automated email from the ASF dual-hosted git repository.

He-Pin pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/pekko.git


The following commit(s) were added to refs/heads/main by this push:
     new 58a9bf0077 perf: use Arrays.copyOf/copyOfRange instead of manual new 
Array + arraycopy (#3150)
58a9bf0077 is described below

commit 58a9bf0077872d7776d926dca01f287529d13df0
Author: He-Pin(kerr) <[email protected]>
AuthorDate: Tue Jun 23 18:43:47 2026 +0800

    perf: use Arrays.copyOf/copyOfRange instead of manual new Array + arraycopy 
(#3150)
    
    * perf: use Arrays.copyOf/copyOfRange instead of manual new Array + 
arraycopy
    
    Motivation:
    Several places allocate a new array then immediately copy from an
    existing array using System.arraycopy. java.util.Arrays.copyOf and
    copyOfRange combine allocation and copy in a single call that the JIT
    can intrinsify more effectively.
    
    Modification:
    - ByteString.scala: 3 sites — clearTemp, resizeTemp, fromArray
      replace new Array[Byte] + System.arraycopy with
      java.util.Arrays.copyOf / copyOfRange
    - ImmutableLongMap.scala: replace full array copy with values.clone()
    - SnapshotSerializer.scala: replace partial copy with
      java.util.Arrays.copyOfRange
    
    Result:
    Fewer lines, potentially faster due to JIT intrinsification of the
    combined allocate+copy operation. ByteString builder operations
    benefit most as they are in frequently-called paths.
    
    Tests:
    sbt "actor/compile" "remote/compile" "persistence/compile" — passed
    
    References:
    Refs #3136
    
    * fix: guard Arrays.copyOf against null _temp in 
ByteStringBuilder.resizeTemp
    
    Motivation:
    `ByteStringBuilder._temp` is initially `null` until the first call to
    `++=` or `putByte`. `resizeTemp` was unconditionally calling
    `java.util.Arrays.copyOf(_temp, size)` which throws
    `NullPointerException` when passed a `null` source array. This bug was
    introduced by the earlier "perf: use Arrays.copyOf/copyOfRange" commit
    in the same PR, which replaced the previous `new Array[Byte](size)`
    followed by `System.arraycopy(src, 0, dst, 0, n)` pattern — the old
    pattern was safe because the arraycopy length was `0` when `_temp` was
    `null`, masking the missing null check.
    
    Modification:
    Guard the call with `if (_temp eq null) new Array[Byte](size) else
    java.util.Arrays.copyOf(_temp, size)`, so the first resize allocates a
    fresh array and subsequent resizes retain the copyOf fast path.
    
    Result:
    `ByteStringBuilder` no longer throws NPE on the very first write when
    the internal buffer has not yet been allocated.
    
    Tests:
    sbt "actor/compile" -- passed
    sbt "actor-tests/Test/testOnly *ByteStringSpec" -- passed (relevant
    directional tests; the NPE was also reachable from stream/Pekko usage).
    
    References:
    Discovered during internal review of PR #3150.
---
 actor/src/main/scala/org/apache/pekko/util/ByteString.scala      | 9 +++------
 .../pekko/persistence/serialization/SnapshotSerializer.scala     | 3 +--
 .../scala/org/apache/pekko/remote/artery/ImmutableLongMap.scala  | 3 +--
 3 files changed, 5 insertions(+), 10 deletions(-)

diff --git a/actor/src/main/scala/org/apache/pekko/util/ByteString.scala 
b/actor/src/main/scala/org/apache/pekko/util/ByteString.scala
index 57c7762ef4..06d0162d50 100644
--- a/actor/src/main/scala/org/apache/pekko/util/ByteString.scala
+++ b/actor/src/main/scala/org/apache/pekko/util/ByteString.scala
@@ -2728,8 +2728,7 @@ object CompactByteString {
     val copyLength = Math.max(Math.min(array.length - copyOffset, length), 0)
     if (copyLength == 0) empty
     else {
-      val copyArray = new Array[Byte](copyLength)
-      System.arraycopy(array, copyOffset, copyArray, 0, copyLength)
+      val copyArray = java.util.Arrays.copyOfRange(array, copyOffset, 
copyOffset + copyLength)
       ByteString.ByteString1C(copyArray)
     }
   }
@@ -2789,16 +2788,14 @@ final class ByteStringBuilder extends Builder[Byte, 
ByteString] {
 
   private def clearTemp(): Unit = {
     if (_tempLength > 0) {
-      val arr = new Array[Byte](_tempLength)
-      System.arraycopy(_temp, 0, arr, 0, _tempLength)
+      val arr = java.util.Arrays.copyOf(_temp, _tempLength)
       _builder += ByteString1(arr)
       _tempLength = 0
     }
   }
 
   private def resizeTemp(size: Int): Unit = {
-    val newtemp = new Array[Byte](size)
-    if (_tempLength > 0) System.arraycopy(_temp, 0, newtemp, 0, _tempLength)
+    val newtemp = if (_temp eq null) new Array[Byte](size) else 
java.util.Arrays.copyOf(_temp, size)
     _temp = newtemp
     _tempCapacity = _temp.length
   }
diff --git 
a/persistence/src/main/scala/org/apache/pekko/persistence/serialization/SnapshotSerializer.scala
 
b/persistence/src/main/scala/org/apache/pekko/persistence/serialization/SnapshotSerializer.scala
index d60b314a62..0e022575de 100644
--- 
a/persistence/src/main/scala/org/apache/pekko/persistence/serialization/SnapshotSerializer.scala
+++ 
b/persistence/src/main/scala/org/apache/pekko/persistence/serialization/SnapshotSerializer.scala
@@ -106,8 +106,7 @@ class SnapshotSerializer(val system: ExtendedActorSystem) 
extends BaseSerializer
     val manifest =
       if (remaining == 0) ""
       else {
-        val manifestBytes = new Array[Byte](remaining)
-        System.arraycopy(bytes, 4, manifestBytes, 0, remaining)
+        val manifestBytes = java.util.Arrays.copyOfRange(bytes, 4, 4 + 
remaining)
         migrateManifestToPekkoIfNecessary(new String(manifestBytes, UTF_8))
       }
     (serializerId, manifest)
diff --git 
a/remote/src/main/scala/org/apache/pekko/remote/artery/ImmutableLongMap.scala 
b/remote/src/main/scala/org/apache/pekko/remote/artery/ImmutableLongMap.scala
index 2b3d7f70ac..2d9eaa410f 100644
--- 
a/remote/src/main/scala/org/apache/pekko/remote/artery/ImmutableLongMap.scala
+++ 
b/remote/src/main/scala/org/apache/pekko/remote/artery/ImmutableLongMap.scala
@@ -69,8 +69,7 @@ private[pekko] class ImmutableLongMap[A >: Null] private 
(private val keys: Arra
       val i = Arrays.binarySearch(keys, key)
       if (i >= 0) {
         // existing key, replace value
-        val newValues = new Array[A](values.length)
-        System.arraycopy(values, 0, newValues, 0, values.length)
+        val newValues = values.clone()
         newValues(i) = value
         new ImmutableLongMap(keys, newValues)
       } else {


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to