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

SteNicholas pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/celeborn.git


The following commit(s) were added to refs/heads/main by this push:
     new 07dde50e40 [CELEBORN-2395] Reduce allocations in Utils.split* methods
07dde50e40 is described below

commit 07dde50e4043e096e385197c53208e21e8d316ce
Author: yew1eb <[email protected]>
AuthorDate: Mon Aug 3 10:34:01 2026 +0800

    [CELEBORN-2395] Reduce allocations in Utils.split* methods
    
    ## What changes were proposed in this pull request?
    
    Rewrite the three `String.split("-")`-based key splitters in `Utils` to 
locate the `-` separator once and slice with `substring`, instead of 
regex-splitting and rejoining:
    
    - `splitShuffleKey` → `lastIndexOf('-')` + two `substring` calls
    - `splitPartitionLocationUniqueId` → `lastIndexOf('-')` + two `substring` 
calls
    - `splitAttemptKey` → `indexOf('-')` + two `substring` calls
    
    This removes the regex compilation, the intermediate `String[]`, the 
`dropRight(1)` array copy, and the `StringBuilder` + new `String` from 
`mkString`.
    
    ## Why are the changes needed?
    
    `splitShuffleKey` is on the per-push-message hot path, called from both 
`recordAppActiveConnection` (default-on per-app metrics) and `checkAuth`. A 
production async-profiler CPU flame graph on `celeborn-worker` (80,611 samples; 
true stack-reconstruction, self sum = 100%) shows the frame's self cost is near 
zero, but its inclusive cost is ~4.23%, of which ~2.95% comes through the 
default-on `recordAppActiveConnection` path and ~1.27% through the always-on 
`checkAuth` path.
    
    <img width="1619" height="260" alt="image" 
src="https://github.com/user-attachments/assets/6a807653-fcdd-4037-8d16-117ebe70046e";
 />
    
    <img width="1686" height="244" alt="image" 
src="https://github.com/user-attachments/assets/240be695-4985-4f8a-abc1-e32c21c6d0ee";
 />
    
    The allocation reduction also lowers young-GC pressure under high push QPS.
    
    ## Does this PR resolve a correctness bug?
    
     No
    
    ## Does this PR introduce any user-facing change?
    
     No
    
    ## How was this patch tested?
    
    - Added unit tests in `UtilsSuite` covering `splitShuffleKey` (including 
`applicationId` containing `-`, plus a `makeShuffleKey` round-trip), 
`splitPartitionLocationUniqueId`, and `splitAttemptKey`.
    - `./build/mvn -pl common -Dtest=UtilsSuite test` — all `split*` tests 
pass. (Two unrelated `CelebornConfSuite` failures — `Fallback to parent 
module's config...` and `rpc_service and rpc_client...` — are pre-existing on a 
clean `upstream/main`; verified by stashing this change and re-running.)
    - `./build/mvn -pl common spotless:check` — clean.
    
    Closes #3771 from yew1eb/CELEBORN-2395.
    
    Authored-by: yew1eb <[email protected]>
    Signed-off-by: Nicholas Jiang <[email protected]>
---
 .../org/apache/celeborn/common/util/Utils.scala    | 23 +++++++++++-----------
 .../apache/celeborn/common/util/UtilsSuite.scala   | 21 ++++++++++++++++++++
 2 files changed, 32 insertions(+), 12 deletions(-)

diff --git a/common/src/main/scala/org/apache/celeborn/common/util/Utils.scala 
b/common/src/main/scala/org/apache/celeborn/common/util/Utils.scala
index 9a634c77d0..da7d69244a 100644
--- a/common/src/main/scala/org/apache/celeborn/common/util/Utils.scala
+++ b/common/src/main/scala/org/apache/celeborn/common/util/Utils.scala
@@ -698,17 +698,17 @@ object Utils extends Logging {
   }
 
   def splitShuffleKey(shuffleKey: String): (String, Int) = {
-    val splits = shuffleKey.split("-")
-    val appId = splits.dropRight(1).mkString("-")
-    val shuffleId = splits.last.toInt
-    (appId, shuffleId)
+    // shuffleId is always the last '-'-delimited segment (see makeShuffleKey),
+    // and applicationId may itself contain '-' (e.g. Spark's 
application_<ts>_<id>),
+    // so locate the last '-' once instead of regex-splitting and rejoining.
+    val idx = shuffleKey.lastIndexOf('-')
+    (shuffleKey.substring(0, idx), shuffleKey.substring(idx + 1).toInt)
   }
 
   def splitPartitionLocationUniqueId(uniqueId: String): (Int, Int) = {
-    val splits = uniqueId.split("-")
-    val partitionId = splits.dropRight(1).mkString("-").toInt
-    val epoch = splits.last.toInt
-    (partitionId, epoch)
+    // epoch is the last segment; partitionId (Int) never contains '-'.
+    val idx = uniqueId.lastIndexOf('-')
+    (uniqueId.substring(0, idx).toInt, uniqueId.substring(idx + 1).toInt)
   }
 
   def makeReducerKey(shuffleId: Int, partitionId: Int): String = {
@@ -728,10 +728,9 @@ object Utils extends Logging {
   }
 
   def splitAttemptKey(attemptKey: String): (Int, Int) = {
-    val splits = attemptKey.split("-")
-    val mapId = splits(0).toInt
-    val attemptId = splits(1).toInt
-    (mapId, attemptId)
+    // Fixed two-segment layout: "<mapId>-<attemptId>", both Int.
+    val idx = attemptKey.indexOf('-')
+    (attemptKey.substring(0, idx).toInt, attemptKey.substring(idx + 1).toInt)
   }
 
   def shuffleKeyPrefix(shuffleKey: String): String = {
diff --git 
a/common/src/test/scala/org/apache/celeborn/common/util/UtilsSuite.scala 
b/common/src/test/scala/org/apache/celeborn/common/util/UtilsSuite.scala
index c83a83b95a..ac4842bf56 100644
--- a/common/src/test/scala/org/apache/celeborn/common/util/UtilsSuite.scala
+++ b/common/src/test/scala/org/apache/celeborn/common/util/UtilsSuite.scala
@@ -138,6 +138,27 @@ class UtilsSuite extends CelebornFunSuite {
     assert((1, 1).equals(Utils.splitPartitionLocationUniqueId("1-1")))
   }
 
+  test("splitShuffleKey") {
+    // plain appId without '-'
+    assert(("app0", 1).equals(Utils.splitShuffleKey("app0-1")))
+    // applicationId containing '-' (e.g. Spark application_<timestamp>_<id>)
+    assert(("application_1690000000000_0001", 7)
+      .equals(Utils.splitShuffleKey("application_1690000000000_0001-7")))
+    // round-trip with makeShuffleKey
+    val key = Utils.makeShuffleKey("application_1690000000000_0001", 42)
+    assert(("application_1690000000000_0001", 
42).equals(Utils.splitShuffleKey(key)))
+  }
+
+  test("splitPartitionLocationUniqueId multiple segments") {
+    // partitionId is an Int and never contains '-'; only the trailing epoch 
is sliced off.
+    assert((123, 5).equals(Utils.splitPartitionLocationUniqueId("123-5")))
+  }
+
+  test("splitAttemptKey") {
+    assert((0, 0).equals(Utils.splitAttemptKey("0-0")))
+    assert((12, 34).equals(Utils.splitAttemptKey("12-34")))
+  }
+
   test("bytesToInt") {
     assert(1229202015 == Utils.bytesToInt(Array(73.toByte, 68.toByte, 
34.toByte, 95.toByte)))
 

Reply via email to