Repository: bazelbuild/bazel
Issue:  Long runtime classpaths slow down unit test execution
Labels: type: bug
team-Performance
untriaged

### Description of the bug:


The classpath pre-processing loop in `java_stub_template.txt` is inefficient for very long classpaths and slows down unit test execution. The slowdown is in this loop in java_stub_template.txt: https://github.com/bazelbuild/bazel/blob/fcfcb929366dd3faac9643302b19c88bcf871ec6/src/main/java/com/google/devtools/build/lib/bazel/rules/java/java_stub_template.txt#L309 for classpaths with ~250,000 and ~700,000 entries the loop takes 28 and 50 seconds, respectively, on an intel MacBook.


### Which category does this issue belong to?


Performance

### What's the simplest, easiest way to reproduce this bug? Please provide a minimal example if possible.


Run a unit test with a very long runtime classpath.

### Which operating system are you running Bazel on?


OSX

### What is the output of `bazel info release`?


release 6.1.0

### If `bazel info release` returns `development version` or `(@non-git)`, tell us how you built Bazel.


_No response_

### What's the output of `git remote get-url origin; git rev-parse master; git rev-parse HEAD` ?


_No response_

### Is this a regression? If yes, please try to identify the Bazel commit where the bug was introduced.


Performance regression seems to have been introduced in https://github.com/bazelbuild/bazel/commit/d0ee889fca77e36f2c76ac07de3048055b8428c9

### Have you found anything relevant by searching the web?

No

### Any other information, logs, or outputs that you want to share?

_No response_

Issue comments:
This is part of the logic that generates a separate jar file as a way to configure classpath that are too long to pass on the command line.

@rsalvador I'm curious which JDK you're using, is it newer than JDK 8? The long term plan here is to replace this logic in the stub template with an `@argument` file: https://github.com/bazelbuild/bazel/issues/6354

(I'm also not sure why the stub template generates this every time it's executed, instead of having Bazel create a separate action that generates the separate jar file once; that came up in https://github.com/bazelbuild/bazel/issues/19036.)
> @rsalvador I'm curious which JDK you're using, is it newer than JDK 8? The long term plan here is to replace this logic in the stub template with an `@argument` file: #6354

@cushon using JDK 11: OpenJDK 64-Bit Server VM (build 11.0.19+7-LTS, mixed mode) by Azul Systems, Inc.
A fix for this issue has been included in [Bazel 6.4.0 RC1](https://releases.bazel.build/6.4.0/rc1/index.html). Please test out the release candidate and report any issues as soon as possible. Thanks!
@iancha1992 verified that the generated test script executables include the fix and that the test execution is faster than with 6.3.2 for a sample test (35 seconds with 6.3.2 vs 12 seconds with 6.4.0rc1)



Pull request review comments:

Patch that resolved the issue:
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/java/java_stub_template.txt b/src/main/java/com/google/devtools/build/lib/bazel/rules/java/java_stub_template.txt
index b3f5070751dfd1..16129cc14dc1b2 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/rules/java/java_stub_template.txt
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/java/java_stub_template.txt
@@ -306,21 +306,24 @@ function create_and_run_classpath_jar() {
 
   OLDIFS="$IFS"
   IFS="${CLASSPATH_SEPARATOR}" # Use a custom separator for the loop.
+  current_dir=$(pwd)
   for path in ${CLASSPATH}; do
     # Loop through the characters of the path and convert characters that are
     # not alphanumeric nor -_.~/ to their 2-digit hexadecimal representation
-    local i c buff
-    local converted_path=""
-
-    for ((i=0; i<${#path}; i++)); do
-      c=${path:$i:1}
-      case ${c} in
-            [-_.~/a-zA-Z0-9] ) buff=${c} ;;
-            * )               printf -v buff '%%%02x' "'$c'"
-      esac
-      converted_path+="${buff}"
-    done
-    path=${converted_path}
+    if [[ ! $path =~ ^[-_.~/a-zA-Z0-9]*$ ]]; then
+      local i c buff
+      local converted_path=""
+
+      for ((i=0; i<${#path}; i++)); do
+        c=${path:$i:1}
+        case ${c} in
+              [-_.~/a-zA-Z0-9] ) buff=${c} ;;
+              * )               printf -v buff '%%%02x' "'$c'"
+        esac
+        converted_path+="${buff}"
+      done
+      path=${converted_path}
+    fi
 
     if is_windows; then
       path="file:/${path}" # e.g. "file:/C:/temp/foo.jar"
@@ -328,7 +331,7 @@ function create_and_run_classpath_jar() {
       # If not absolute, qualify the path
       case "${path}" in
         /*) ;; # Already an absolute path
-        *) path="$(pwd)/${path}";; # Now qualified
+        *) path="${current_dir}/${path}";; # Now qualified
       esac
       path="file:${path}" # e.g. "file:/usr/local/foo.jar"
     fi


Test patch: