Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve performance of StringUtils.isMixedCase() #1096

Merged

Conversation

hduelme
Copy link
Contributor

@hduelme hduelme commented Aug 14, 2023

The changes optimize the mixed-case detecting function in StringUtils.java by reducing redundant character property checks. The loop will now return early when both uppercase and lowercase are present, preventing further unnecessary operations. It also introduces a local variable to store the character being checked, reducing the number of method calls to get the character. The end return statement is also simplified to return false, as the true condition has already been covered inside the loop.

@garydgregory
Copy link
Member

How do you know it's faster? Sometimes running a JMH test shows surprising results, and sometimes not ;-)

@hduelme
Copy link
Contributor Author

hduelme commented Aug 14, 2023

In the best case only two iterations are needed instead of three. Whenever the string is mixed cases, it should take one iteration less. In the wirst case the performance should stay the same. But I agree, I should provide some data which support my theory.

@garydgregory
Copy link
Member

You could add a JMH benchmark to your PR

@hduelme
Copy link
Contributor Author

hduelme commented Aug 14, 2023

@garydgregory as you surgeste, I wrote a small test.
Here the results

/home/hduelme/.jdks/temurin-1.8.0_362/bin/java -javaagent:/home/hduelme/.local/share/JetBrains/Toolbox/apps/intellij-idea-ultimate/lib/idea_rt.jar=38469:/home/hduelme/.local/share/JetBrains/Toolbox/apps/intellij-idea-ultimate/bin -Dfile.encoding=UTF-8 -classpath /home/hduelme/IdeaProjects/commons-lang/target/test-classes:/home/hduelme/IdeaProjects/commons-lang/target/classes:/home/hduelme/.m2/repository/org/junit/jupiter/junit-jupiter/5.9.3/junit-jupiter-5.9.3.jar:/home/hduelme/.m2/repository/org/junit/jupiter/junit-jupiter-api/5.9.3/junit-jupiter-api-5.9.3.jar:/home/hduelme/.m2/repository/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0.jar:/home/hduelme/.m2/repository/org/junit/platform/junit-platform-commons/1.9.3/junit-platform-commons-1.9.3.jar:/home/hduelme/.m2/repository/org/apiguardian/apiguardian-api/1.1.2/apiguardian-api-1.1.2.jar:/home/hduelme/.m2/repository/org/junit/jupiter/junit-jupiter-params/5.9.3/junit-jupiter-params-5.9.3.jar:/home/hduelme/.m2/repository/org/junit/jupiter/junit-jupiter-engine/5.9.3/junit-jupiter-engine-5.9.3.jar:/home/hduelme/.m2/repository/org/junit/platform/junit-platform-engine/1.9.3/junit-platform-engine-1.9.3.jar:/home/hduelme/.m2/repository/org/junit-pioneer/junit-pioneer/1.9.1/junit-pioneer-1.9.1.jar:/home/hduelme/.m2/repository/org/junit/platform/junit-platform-launcher/1.9.3/junit-platform-launcher-1.9.3.jar:/home/hduelme/.m2/repository/org/hamcrest/hamcrest/2.2/hamcrest-2.2.jar:/home/hduelme/.m2/repository/org/easymock/easymock/5.1.0/easymock-5.1.0.jar:/home/hduelme/.m2/repository/org/objenesis/objenesis/3.3/objenesis-3.3.jar:/home/hduelme/.m2/repository/org/apache/commons/commons-text/1.10.0/commons-text-1.10.0.jar:/home/hduelme/.m2/repository/org/apache/commons/commons-lang3/3.12.0/commons-lang3-3.12.0.jar:/home/hduelme/.m2/repository/org/openjdk/jmh/jmh-core/1.37/jmh-core-1.37.jar:/home/hduelme/.m2/repository/net/sf/jopt-simple/jopt-simple/5.0.4/jopt-simple-5.0.4.jar:/home/hduelme/.m2/repository/org/apache/commons/commons-math3/3.6.1/commons-math3-3.6.1.jar:/home/hduelme/.m2/repository/org/openjdk/jmh/jmh-generator-annprocess/1.37/jmh-generator-annprocess-1.37.jar:/home/hduelme/.m2/repository/com/google/code/findbugs/jsr305/3.0.2/jsr305-3.0.2.jar org.openjdk.jmh.Main org.apache.commons.lang3.StringUtilsIsMixedCaseTest.*
# JMH version: 1.37
# VM version: JDK 1.8.0_362, OpenJDK 64-Bit Server VM, 25.362-b09
# VM invoker: /home/hduelme/.jdks/temurin-1.8.0_362/jre/bin/java
# VM options: -javaagent:/home/hduelme/.local/share/JetBrains/Toolbox/apps/intellij-idea-ultimate/lib/idea_rt.jar=38469:/home/hduelme/.local/share/JetBrains/Toolbox/apps/intellij-idea-ultimate/bin -Dfile.encoding=UTF-8
# Blackhole mode: full + dont-inline hint (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
# Warmup: 5 iterations, 10 s each
# Measurement: 5 iterations, 10 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Average time, time/op
# Benchmark: org.apache.commons.lang3.StringUtilsIsMixedCaseTest.newIsMixedCaseBeginningMatch

# Run progress: 0.00% complete, ETA 01:06:40
# Fork: 1 of 5
# Warmup Iteration   1: 6.154 ns/op
# Warmup Iteration   2: 6.144 ns/op
# Warmup Iteration   3: 4.745 ns/op
# Warmup Iteration   4: 4.752 ns/op
# Warmup Iteration   5: 4.767 ns/op
Iteration   1: 4.751 ns/op
Iteration   2: 4.752 ns/op
Iteration   3: 4.761 ns/op
Iteration   4: 4.753 ns/op
Iteration   5: 4.749 ns/op

# Run progress: 2.50% complete, ETA 01:05:24
# Fork: 2 of 5
# Warmup Iteration   1: 6.260 ns/op
# Warmup Iteration   2: 6.148 ns/op
# Warmup Iteration   3: 4.458 ns/op
# Warmup Iteration   4: 4.455 ns/op
# Warmup Iteration   5: 4.456 ns/op
Iteration   1: 4.464 ns/op
Iteration   2: 4.454 ns/op
Iteration   3: 4.456 ns/op
Iteration   4: 4.458 ns/op
Iteration   5: 4.474 ns/op

# Run progress: 5.00% complete, ETA 01:03:42
# Fork: 3 of 5
# Warmup Iteration   1: 6.258 ns/op
# Warmup Iteration   2: 6.128 ns/op
# Warmup Iteration   3: 4.469 ns/op
# Warmup Iteration   4: 4.471 ns/op
# Warmup Iteration   5: 4.459 ns/op
Iteration   1: 4.473 ns/op
Iteration   2: 4.468 ns/op
Iteration   3: 4.469 ns/op
Iteration   4: 4.469 ns/op
Iteration   5: 4.455 ns/op

# Run progress: 7.50% complete, ETA 01:02:01
# Fork: 4 of 5
# Warmup Iteration   1: 7.002 ns/op
# Warmup Iteration   2: 6.128 ns/op
# Warmup Iteration   3: 4.456 ns/op
# Warmup Iteration   4: 4.477 ns/op
# Warmup Iteration   5: 4.463 ns/op
Iteration   1: 4.460 ns/op
Iteration   2: 4.461 ns/op
Iteration   3: 4.456 ns/op
Iteration   4: 4.454 ns/op
Iteration   5: 4.456 ns/op

# Run progress: 10.00% complete, ETA 01:00:20
# Fork: 5 of 5
# Warmup Iteration   1: 6.256 ns/op
# Warmup Iteration   2: 6.131 ns/op
# Warmup Iteration   3: 4.458 ns/op
# Warmup Iteration   4: 4.478 ns/op
# Warmup Iteration   5: 4.476 ns/op
Iteration   1: 4.475 ns/op
Iteration   2: 4.468 ns/op
Iteration   3: 4.463 ns/op
Iteration   4: 4.457 ns/op
Iteration   5: 4.461 ns/op


Result "org.apache.commons.lang3.StringUtilsIsMixedCaseTest.newIsMixedCaseBeginningMatch":
  4.521 ±(99.9%) 0.089 ns/op [Average]
  (min, avg, max) = (4.454, 4.521, 4.761), stdev = 0.119
  CI (99.9%): [4.432, 4.610] (assumes normal distribution)


# JMH version: 1.37
# VM version: JDK 1.8.0_362, OpenJDK 64-Bit Server VM, 25.362-b09
# VM invoker: /home/hduelme/.jdks/temurin-1.8.0_362/jre/bin/java
# VM options: -javaagent:/home/hduelme/.local/share/JetBrains/Toolbox/apps/intellij-idea-ultimate/lib/idea_rt.jar=38469:/home/hduelme/.local/share/JetBrains/Toolbox/apps/intellij-idea-ultimate/bin -Dfile.encoding=UTF-8
# Blackhole mode: full + dont-inline hint (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
# Warmup: 5 iterations, 10 s each
# Measurement: 5 iterations, 10 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Average time, time/op
# Benchmark: org.apache.commons.lang3.StringUtilsIsMixedCaseTest.newIsMixedCaseEndMatch

# Run progress: 12.50% complete, ETA 00:58:39
# Fork: 1 of 5
# Warmup Iteration   1: 26.808 ns/op
# Warmup Iteration   2: 19.734 ns/op
# Warmup Iteration   3: 28.725 ns/op
# Warmup Iteration   4: 28.759 ns/op
# Warmup Iteration   5: 28.709 ns/op
Iteration   1: 28.673 ns/op
Iteration   2: 28.670 ns/op
Iteration   3: 28.657 ns/op
Iteration   4: 28.672 ns/op
Iteration   5: 28.644 ns/op

# Run progress: 15.00% complete, ETA 00:56:59
# Fork: 2 of 5
# Warmup Iteration   1: 26.805 ns/op
# Warmup Iteration   2: 19.854 ns/op
# Warmup Iteration   3: 28.770 ns/op
# Warmup Iteration   4: 28.703 ns/op
# Warmup Iteration   5: 28.683 ns/op
Iteration   1: 28.687 ns/op
Iteration   2: 28.702 ns/op
Iteration   3: 28.723 ns/op
Iteration   4: 28.834 ns/op
Iteration   5: 28.735 ns/op

# Run progress: 17.50% complete, ETA 00:55:18
# Fork: 3 of 5
# Warmup Iteration   1: 27.197 ns/op
# Warmup Iteration   2: 19.838 ns/op
# Warmup Iteration   3: 28.919 ns/op
# Warmup Iteration   4: 28.917 ns/op
# Warmup Iteration   5: 28.904 ns/op
Iteration   1: 28.922 ns/op
Iteration   2: 28.924 ns/op
Iteration   3: 28.871 ns/op
Iteration   4: 28.813 ns/op
Iteration   5: 28.951 ns/op

# Run progress: 20.00% complete, ETA 00:53:38
# Fork: 4 of 5
# Warmup Iteration   1: 26.772 ns/op
# Warmup Iteration   2: 19.782 ns/op
# Warmup Iteration   3: 28.762 ns/op
# Warmup Iteration   4: 28.760 ns/op
# Warmup Iteration   5: 28.703 ns/op
Iteration   1: 28.689 ns/op
Iteration   2: 28.681 ns/op
Iteration   3: 28.698 ns/op
Iteration   4: 28.687 ns/op
Iteration   5: 28.685 ns/op

# Run progress: 22.50% complete, ETA 00:51:57
# Fork: 5 of 5
# Warmup Iteration   1: 26.797 ns/op
# Warmup Iteration   2: 19.813 ns/op
# Warmup Iteration   3: 28.716 ns/op
# Warmup Iteration   4: 28.734 ns/op
# Warmup Iteration   5: 28.884 ns/op
Iteration   1: 28.749 ns/op
Iteration   2: 28.721 ns/op
Iteration   3: 28.726 ns/op
Iteration   4: 28.842 ns/op
Iteration   5: 28.707 ns/op


Result "org.apache.commons.lang3.StringUtilsIsMixedCaseTest.newIsMixedCaseEndMatch":
  28.746 ±(99.9%) 0.069 ns/op [Average]
  (min, avg, max) = (28.644, 28.746, 28.951), stdev = 0.092
  CI (99.9%): [28.678, 28.815] (assumes normal distribution)


# JMH version: 1.37
# VM version: JDK 1.8.0_362, OpenJDK 64-Bit Server VM, 25.362-b09
# VM invoker: /home/hduelme/.jdks/temurin-1.8.0_362/jre/bin/java
# VM options: -javaagent:/home/hduelme/.local/share/JetBrains/Toolbox/apps/intellij-idea-ultimate/lib/idea_rt.jar=38469:/home/hduelme/.local/share/JetBrains/Toolbox/apps/intellij-idea-ultimate/bin -Dfile.encoding=UTF-8
# Blackhole mode: full + dont-inline hint (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
# Warmup: 5 iterations, 10 s each
# Measurement: 5 iterations, 10 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Average time, time/op
# Benchmark: org.apache.commons.lang3.StringUtilsIsMixedCaseTest.newIsMixedCaseMiddleMatch

# Run progress: 25.00% complete, ETA 00:50:17
# Fork: 1 of 5
# Warmup Iteration   1: 15.312 ns/op
# Warmup Iteration   2: 12.293 ns/op
# Warmup Iteration   3: 14.249 ns/op
# Warmup Iteration   4: 14.174 ns/op
# Warmup Iteration   5: 14.212 ns/op
Iteration   1: 14.195 ns/op
Iteration   2: 14.259 ns/op
Iteration   3: 14.197 ns/op
Iteration   4: 14.459 ns/op
Iteration   5: 14.409 ns/op

# Run progress: 27.50% complete, ETA 00:48:36
# Fork: 2 of 5
# Warmup Iteration   1: 15.696 ns/op
# Warmup Iteration   2: 12.001 ns/op
# Warmup Iteration   3: 14.163 ns/op
# Warmup Iteration   4: 14.155 ns/op
# Warmup Iteration   5: 14.156 ns/op
Iteration   1: 14.145 ns/op
Iteration   2: 14.101 ns/op
Iteration   3: 14.168 ns/op
Iteration   4: 14.122 ns/op
Iteration   5: 14.105 ns/op

# Run progress: 30.00% complete, ETA 00:46:55
# Fork: 3 of 5
# Warmup Iteration   1: 15.357 ns/op
# Warmup Iteration   2: 12.311 ns/op
# Warmup Iteration   3: 14.222 ns/op
# Warmup Iteration   4: 14.159 ns/op
# Warmup Iteration   5: 14.142 ns/op
Iteration   1: 14.056 ns/op
Iteration   2: 14.096 ns/op
Iteration   3: 14.050 ns/op
Iteration   4: 14.085 ns/op
Iteration   5: 14.053 ns/op

# Run progress: 32.50% complete, ETA 00:45:15
# Fork: 4 of 5
# Warmup Iteration   1: 15.104 ns/op
# Warmup Iteration   2: 12.046 ns/op
# Warmup Iteration   3: 14.087 ns/op
# Warmup Iteration   4: 14.103 ns/op
# Warmup Iteration   5: 14.057 ns/op
Iteration   1: 14.067 ns/op
Iteration   2: 14.072 ns/op
Iteration   3: 14.085 ns/op
Iteration   4: 14.101 ns/op
Iteration   5: 14.086 ns/op

# Run progress: 35.00% complete, ETA 00:43:34
# Fork: 5 of 5
# Warmup Iteration   1: 15.144 ns/op
# Warmup Iteration   2: 12.075 ns/op
# Warmup Iteration   3: 14.110 ns/op
# Warmup Iteration   4: 14.142 ns/op
# Warmup Iteration   5: 14.121 ns/op
Iteration   1: 14.117 ns/op
Iteration   2: 14.133 ns/op
Iteration   3: 14.095 ns/op
Iteration   4: 14.209 ns/op
Iteration   5: 14.118 ns/op


Result "org.apache.commons.lang3.StringUtilsIsMixedCaseTest.newIsMixedCaseMiddleMatch":
  14.143 ±(99.9%) 0.077 ns/op [Average]
  (min, avg, max) = (14.050, 14.143, 14.459), stdev = 0.102
  CI (99.9%): [14.067, 14.220] (assumes normal distribution)


# JMH version: 1.37
# VM version: JDK 1.8.0_362, OpenJDK 64-Bit Server VM, 25.362-b09
# VM invoker: /home/hduelme/.jdks/temurin-1.8.0_362/jre/bin/java
# VM options: -javaagent:/home/hduelme/.local/share/JetBrains/Toolbox/apps/intellij-idea-ultimate/lib/idea_rt.jar=38469:/home/hduelme/.local/share/JetBrains/Toolbox/apps/intellij-idea-ultimate/bin -Dfile.encoding=UTF-8
# Blackhole mode: full + dont-inline hint (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
# Warmup: 5 iterations, 10 s each
# Measurement: 5 iterations, 10 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Average time, time/op
# Benchmark: org.apache.commons.lang3.StringUtilsIsMixedCaseTest.newIsMixedCaseNoneMatch

# Run progress: 37.50% complete, ETA 00:41:54
# Fork: 1 of 5
# Warmup Iteration   1: 70.563 ns/op
# Warmup Iteration   2: 70.388 ns/op
# Warmup Iteration   3: 50.391 ns/op
# Warmup Iteration   4: 50.451 ns/op
# Warmup Iteration   5: 50.437 ns/op
Iteration   1: 50.482 ns/op
Iteration   2: 50.394 ns/op
Iteration   3: 50.395 ns/op
Iteration   4: 50.375 ns/op
Iteration   5: 50.407 ns/op

# Run progress: 40.00% complete, ETA 00:40:13
# Fork: 2 of 5
# Warmup Iteration   1: 70.549 ns/op
# Warmup Iteration   2: 70.368 ns/op
# Warmup Iteration   3: 51.116 ns/op
# Warmup Iteration   4: 51.021 ns/op
# Warmup Iteration   5: 51.174 ns/op
Iteration   1: 51.157 ns/op
Iteration   2: 50.922 ns/op
Iteration   3: 51.075 ns/op
Iteration   4: 51.081 ns/op
Iteration   5: 51.198 ns/op

# Run progress: 42.50% complete, ETA 00:38:32
# Fork: 3 of 5
# Warmup Iteration   1: 70.523 ns/op
# Warmup Iteration   2: 70.378 ns/op
# Warmup Iteration   3: 50.503 ns/op
# Warmup Iteration   4: 50.411 ns/op
# Warmup Iteration   5: 50.407 ns/op
Iteration   1: 50.401 ns/op
Iteration   2: 50.702 ns/op
Iteration   3: 50.447 ns/op
Iteration   4: 50.392 ns/op
Iteration   5: 50.381 ns/op

# Run progress: 45.00% complete, ETA 00:36:52
# Fork: 4 of 5
# Warmup Iteration   1: 70.680 ns/op
# Warmup Iteration   2: 70.607 ns/op
# Warmup Iteration   3: 50.591 ns/op
# Warmup Iteration   4: 50.451 ns/op
# Warmup Iteration   5: 50.573 ns/op
Iteration   1: 51.319 ns/op
Iteration   2: 50.672 ns/op
Iteration   3: 50.496 ns/op
Iteration   4: 50.551 ns/op
Iteration   5: 50.576 ns/op

# Run progress: 47.50% complete, ETA 00:35:11
# Fork: 5 of 5
# Warmup Iteration   1: 70.528 ns/op
# Warmup Iteration   2: 70.427 ns/op
# Warmup Iteration   3: 50.416 ns/op
# Warmup Iteration   4: 50.365 ns/op
# Warmup Iteration   5: 50.360 ns/op
Iteration   1: 50.420 ns/op
Iteration   2: 50.424 ns/op
Iteration   3: 50.368 ns/op
Iteration   4: 50.442 ns/op
Iteration   5: 50.362 ns/op


Result "org.apache.commons.lang3.StringUtilsIsMixedCaseTest.newIsMixedCaseNoneMatch":
  50.617 ±(99.9%) 0.232 ns/op [Average]
  (min, avg, max) = (50.362, 50.617, 51.319), stdev = 0.310
  CI (99.9%): [50.385, 50.850] (assumes normal distribution)


# JMH version: 1.37
# VM version: JDK 1.8.0_362, OpenJDK 64-Bit Server VM, 25.362-b09
# VM invoker: /home/hduelme/.jdks/temurin-1.8.0_362/jre/bin/java
# VM options: -javaagent:/home/hduelme/.local/share/JetBrains/Toolbox/apps/intellij-idea-ultimate/lib/idea_rt.jar=38469:/home/hduelme/.local/share/JetBrains/Toolbox/apps/intellij-idea-ultimate/bin -Dfile.encoding=UTF-8
# Blackhole mode: full + dont-inline hint (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
# Warmup: 5 iterations, 10 s each
# Measurement: 5 iterations, 10 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Average time, time/op
# Benchmark: org.apache.commons.lang3.StringUtilsIsMixedCaseTest.oldIsMixedCaseBeginningMatch

# Run progress: 50.00% complete, ETA 00:33:31
# Fork: 1 of 5
# Warmup Iteration   1: 9.442 ns/op
# Warmup Iteration   2: 7.279 ns/op
# Warmup Iteration   3: 5.311 ns/op
# Warmup Iteration   4: 5.312 ns/op
# Warmup Iteration   5: 5.317 ns/op
Iteration   1: 5.312 ns/op
Iteration   2: 5.310 ns/op
Iteration   3: 5.305 ns/op
Iteration   4: 5.296 ns/op
Iteration   5: 5.301 ns/op

# Run progress: 52.50% complete, ETA 00:31:50
# Fork: 2 of 5
# Warmup Iteration   1: 8.650 ns/op
# Warmup Iteration   2: 7.253 ns/op
# Warmup Iteration   3: 5.309 ns/op
# Warmup Iteration   4: 5.292 ns/op
# Warmup Iteration   5: 5.300 ns/op
Iteration   1: 5.311 ns/op
Iteration   2: 5.301 ns/op
Iteration   3: 5.322 ns/op
Iteration   4: 5.309 ns/op
Iteration   5: 5.348 ns/op

# Run progress: 55.00% complete, ETA 00:30:10
# Fork: 3 of 5
# Warmup Iteration   1: 9.520 ns/op
# Warmup Iteration   2: 7.302 ns/op
# Warmup Iteration   3: 5.305 ns/op
# Warmup Iteration   4: 5.413 ns/op
# Warmup Iteration   5: 5.289 ns/op
Iteration   1: 5.290 ns/op
Iteration   2: 5.302 ns/op
Iteration   3: 5.292 ns/op
Iteration   4: 5.291 ns/op
Iteration   5: 5.300 ns/op

# Run progress: 57.50% complete, ETA 00:28:29
# Fork: 4 of 5
# Warmup Iteration   1: 8.836 ns/op
# Warmup Iteration   2: 7.252 ns/op
# Warmup Iteration   3: 5.303 ns/op
# Warmup Iteration   4: 5.309 ns/op
# Warmup Iteration   5: 5.302 ns/op
Iteration   1: 5.292 ns/op
Iteration   2: 5.331 ns/op
Iteration   3: 5.357 ns/op
Iteration   4: 5.406 ns/op
Iteration   5: 5.340 ns/op

# Run progress: 60.00% complete, ETA 00:26:48
# Fork: 5 of 5
# Warmup Iteration   1: 7.761 ns/op
# Warmup Iteration   2: 8.117 ns/op
# Warmup Iteration   3: 7.011 ns/op
# Warmup Iteration   4: 7.120 ns/op
# Warmup Iteration   5: 7.040 ns/op
Iteration   1: 6.999 ns/op
Iteration   2: 7.015 ns/op
Iteration   3: 7.024 ns/op
Iteration   4: 7.020 ns/op
Iteration   5: 7.011 ns/op


Result "org.apache.commons.lang3.StringUtilsIsMixedCaseTest.oldIsMixedCaseBeginningMatch":
  5.655 ±(99.9%) 0.520 ns/op [Average]
  (min, avg, max) = (5.290, 5.655, 7.024), stdev = 0.694
  CI (99.9%): [5.136, 6.175] (assumes normal distribution)


# JMH version: 1.37
# VM version: JDK 1.8.0_362, OpenJDK 64-Bit Server VM, 25.362-b09
# VM invoker: /home/hduelme/.jdks/temurin-1.8.0_362/jre/bin/java
# VM options: -javaagent:/home/hduelme/.local/share/JetBrains/Toolbox/apps/intellij-idea-ultimate/lib/idea_rt.jar=38469:/home/hduelme/.local/share/JetBrains/Toolbox/apps/intellij-idea-ultimate/bin -Dfile.encoding=UTF-8
# Blackhole mode: full + dont-inline hint (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
# Warmup: 5 iterations, 10 s each
# Measurement: 5 iterations, 10 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Average time, time/op
# Benchmark: org.apache.commons.lang3.StringUtilsIsMixedCaseTest.oldIsMixedCaseEndMatch

# Run progress: 62.50% complete, ETA 00:25:08
# Fork: 1 of 5
# Warmup Iteration   1: 42.271 ns/op
# Warmup Iteration   2: 43.333 ns/op
# Warmup Iteration   3: 33.665 ns/op
# Warmup Iteration   4: 33.696 ns/op
# Warmup Iteration   5: 33.678 ns/op
Iteration   1: 33.692 ns/op
Iteration   2: 33.685 ns/op
Iteration   3: 33.666 ns/op
Iteration   4: 33.659 ns/op
Iteration   5: 33.662 ns/op

# Run progress: 65.00% complete, ETA 00:23:27
# Fork: 2 of 5
# Warmup Iteration   1: 36.642 ns/op
# Warmup Iteration   2: 42.727 ns/op
# Warmup Iteration   3: 37.103 ns/op
# Warmup Iteration   4: 37.105 ns/op
# Warmup Iteration   5: 37.084 ns/op
Iteration   1: 37.107 ns/op
Iteration   2: 37.058 ns/op
Iteration   3: 37.022 ns/op
Iteration   4: 37.045 ns/op
Iteration   5: 37.110 ns/op

# Run progress: 67.50% complete, ETA 00:21:47
# Fork: 3 of 5
# Warmup Iteration   1: 36.601 ns/op
# Warmup Iteration   2: 42.661 ns/op
# Warmup Iteration   3: 36.406 ns/op
# Warmup Iteration   4: 36.379 ns/op
# Warmup Iteration   5: 36.441 ns/op
Iteration   1: 36.440 ns/op
Iteration   2: 36.617 ns/op
Iteration   3: 36.487 ns/op
Iteration   4: 36.597 ns/op
Iteration   5: 36.476 ns/op

# Run progress: 70.00% complete, ETA 00:20:06
# Fork: 4 of 5
# Warmup Iteration   1: 36.565 ns/op
# Warmup Iteration   2: 42.933 ns/op
# Warmup Iteration   3: 36.266 ns/op
# Warmup Iteration   4: 36.434 ns/op
# Warmup Iteration   5: 36.407 ns/op
Iteration   1: 36.436 ns/op
Iteration   2: 36.214 ns/op
Iteration   3: 36.289 ns/op
Iteration   4: 36.400 ns/op
Iteration   5: 36.430 ns/op

# Run progress: 72.50% complete, ETA 00:18:26
# Fork: 5 of 5
# Warmup Iteration   1: 36.686 ns/op
# Warmup Iteration   2: 43.143 ns/op
# Warmup Iteration   3: 36.252 ns/op
# Warmup Iteration   4: 36.161 ns/op
# Warmup Iteration   5: 36.312 ns/op
Iteration   1: 36.316 ns/op
Iteration   2: 36.177 ns/op
Iteration   3: 36.214 ns/op
Iteration   4: 36.308 ns/op
Iteration   5: 36.237 ns/op


Result "org.apache.commons.lang3.StringUtilsIsMixedCaseTest.oldIsMixedCaseEndMatch":
  35.974 ±(99.9%) 0.907 ns/op [Average]
  (min, avg, max) = (33.659, 35.974, 37.110), stdev = 1.210
  CI (99.9%): [35.067, 36.880] (assumes normal distribution)


# JMH version: 1.37
# VM version: JDK 1.8.0_362, OpenJDK 64-Bit Server VM, 25.362-b09
# VM invoker: /home/hduelme/.jdks/temurin-1.8.0_362/jre/bin/java
# VM options: -javaagent:/home/hduelme/.local/share/JetBrains/Toolbox/apps/intellij-idea-ultimate/lib/idea_rt.jar=38469:/home/hduelme/.local/share/JetBrains/Toolbox/apps/intellij-idea-ultimate/bin -Dfile.encoding=UTF-8
# Blackhole mode: full + dont-inline hint (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
# Warmup: 5 iterations, 10 s each
# Measurement: 5 iterations, 10 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Average time, time/op
# Benchmark: org.apache.commons.lang3.StringUtilsIsMixedCaseTest.oldIsMixedCaseMiddleMatch

# Run progress: 75.00% complete, ETA 00:16:45
# Fork: 1 of 5
# Warmup Iteration   1: 19.964 ns/op
# Warmup Iteration   2: 20.020 ns/op
# Warmup Iteration   3: 15.399 ns/op
# Warmup Iteration   4: 15.371 ns/op
# Warmup Iteration   5: 15.392 ns/op
Iteration   1: 15.440 ns/op
Iteration   2: 15.400 ns/op
Iteration   3: 15.365 ns/op
Iteration   4: 15.384 ns/op
Iteration   5: 15.373 ns/op

# Run progress: 77.50% complete, ETA 00:15:05
# Fork: 2 of 5
# Warmup Iteration   1: 16.515 ns/op
# Warmup Iteration   2: 20.036 ns/op
# Warmup Iteration   3: 15.277 ns/op
# Warmup Iteration   4: 15.237 ns/op
# Warmup Iteration   5: 15.185 ns/op
Iteration   1: 15.132 ns/op
Iteration   2: 15.176 ns/op
Iteration   3: 15.198 ns/op
Iteration   4: 15.204 ns/op
Iteration   5: 15.230 ns/op

# Run progress: 80.00% complete, ETA 00:13:24
# Fork: 3 of 5
# Warmup Iteration   1: 19.751 ns/op
# Warmup Iteration   2: 20.010 ns/op
# Warmup Iteration   3: 15.241 ns/op
# Warmup Iteration   4: 15.460 ns/op
# Warmup Iteration   5: 15.286 ns/op
Iteration   1: 15.191 ns/op
Iteration   2: 15.282 ns/op
Iteration   3: 15.206 ns/op
Iteration   4: 15.189 ns/op
Iteration   5: 15.187 ns/op

# Run progress: 82.50% complete, ETA 00:11:43
# Fork: 4 of 5
# Warmup Iteration   1: 19.742 ns/op
# Warmup Iteration   2: 20.189 ns/op
# Warmup Iteration   3: 15.367 ns/op
# Warmup Iteration   4: 15.434 ns/op
# Warmup Iteration   5: 15.451 ns/op
Iteration   1: 15.397 ns/op
Iteration   2: 15.307 ns/op
Iteration   3: 15.375 ns/op
Iteration   4: 15.314 ns/op
Iteration   5: 15.310 ns/op

# Run progress: 85.00% complete, ETA 00:10:03
# Fork: 5 of 5
# Warmup Iteration   1: 20.045 ns/op
# Warmup Iteration   2: 20.070 ns/op
# Warmup Iteration   3: 15.327 ns/op
# Warmup Iteration   4: 15.315 ns/op
# Warmup Iteration   5: 15.340 ns/op
Iteration   1: 15.222 ns/op
Iteration   2: 15.209 ns/op
Iteration   3: 15.173 ns/op
Iteration   4: 15.244 ns/op
Iteration   5: 15.249 ns/op


Result "org.apache.commons.lang3.StringUtilsIsMixedCaseTest.oldIsMixedCaseMiddleMatch":
  15.270 ±(99.9%) 0.067 ns/op [Average]
  (min, avg, max) = (15.132, 15.270, 15.440), stdev = 0.089
  CI (99.9%): [15.204, 15.337] (assumes normal distribution)


# JMH version: 1.37
# VM version: JDK 1.8.0_362, OpenJDK 64-Bit Server VM, 25.362-b09
# VM invoker: /home/hduelme/.jdks/temurin-1.8.0_362/jre/bin/java
# VM options: -javaagent:/home/hduelme/.local/share/JetBrains/Toolbox/apps/intellij-idea-ultimate/lib/idea_rt.jar=38469:/home/hduelme/.local/share/JetBrains/Toolbox/apps/intellij-idea-ultimate/bin -Dfile.encoding=UTF-8
# Blackhole mode: full + dont-inline hint (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
# Warmup: 5 iterations, 10 s each
# Measurement: 5 iterations, 10 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Average time, time/op
# Benchmark: org.apache.commons.lang3.StringUtilsIsMixedCaseTest.oldIsMixedCaseNoneMatch

# Run progress: 87.50% complete, ETA 00:08:22
# Fork: 1 of 5
# Warmup Iteration   1: 73.396 ns/op
# Warmup Iteration   2: 73.192 ns/op
# Warmup Iteration   3: 73.192 ns/op
# Warmup Iteration   4: 73.222 ns/op
# Warmup Iteration   5: 73.186 ns/op
Iteration   1: 73.298 ns/op
Iteration   2: 73.185 ns/op
Iteration   3: 73.204 ns/op
Iteration   4: 73.200 ns/op
Iteration   5: 73.202 ns/op

# Run progress: 90.00% complete, ETA 00:06:42
# Fork: 2 of 5
# Warmup Iteration   1: 73.385 ns/op
# Warmup Iteration   2: 80.468 ns/op
# Warmup Iteration   3: 73.504 ns/op
# Warmup Iteration   4: 73.408 ns/op
# Warmup Iteration   5: 73.587 ns/op
Iteration   1: 73.543 ns/op
Iteration   2: 73.358 ns/op
Iteration   3: 73.584 ns/op
Iteration   4: 73.447 ns/op
Iteration   5: 73.605 ns/op

# Run progress: 92.50% complete, ETA 00:05:01
# Fork: 3 of 5
# Warmup Iteration   1: 73.718 ns/op
# Warmup Iteration   2: 73.194 ns/op
# Warmup Iteration   3: 73.207 ns/op
# Warmup Iteration   4: 73.200 ns/op
# Warmup Iteration   5: 73.188 ns/op
Iteration   1: 73.222 ns/op
Iteration   2: 73.218 ns/op
Iteration   3: 73.440 ns/op
Iteration   4: 73.442 ns/op
Iteration   5: 73.430 ns/op

# Run progress: 95.00% complete, ETA 00:03:21
# Fork: 4 of 5
# Warmup Iteration   1: 73.404 ns/op
# Warmup Iteration   2: 73.236 ns/op
# Warmup Iteration   3: 73.223 ns/op
# Warmup Iteration   4: 73.221 ns/op
# Warmup Iteration   5: 73.457 ns/op
Iteration   1: 73.476 ns/op
Iteration   2: 73.180 ns/op
Iteration   3: 73.283 ns/op
Iteration   4: 73.259 ns/op
Iteration   5: 73.434 ns/op

# Run progress: 97.50% complete, ETA 00:01:40
# Fork: 5 of 5
# Warmup Iteration   1: 73.345 ns/op
# Warmup Iteration   2: 73.432 ns/op
# Warmup Iteration   3: 73.227 ns/op
# Warmup Iteration   4: 73.269 ns/op
# Warmup Iteration   5: 73.246 ns/op
Iteration   1: 73.227 ns/op
Iteration   2: 73.204 ns/op
Iteration   3: 73.227 ns/op
Iteration   4: 73.360 ns/op
Iteration   5: 73.240 ns/op


Result "org.apache.commons.lang3.StringUtilsIsMixedCaseTest.oldIsMixedCaseNoneMatch":
  73.331 ±(99.9%) 0.101 ns/op [Average]
  (min, avg, max) = (73.180, 73.331, 73.605), stdev = 0.135
  CI (99.9%): [73.230, 73.432] (assumes normal distribution)


# Run complete. Total time: 01:07:02

REMEMBER: The numbers below are just data. To gain reusable insights, you need to follow up on
why the numbers are the way they are. Use profilers (see -prof, -lprof), design factorial
experiments, perform baseline and negative tests that provide experimental control, make sure
the benchmarking environment is safe on JVM/OS/HW level, ask for reviews from the domain experts.
Do not assume the numbers tell you what you want them to tell.

Benchmark                                                Mode  Cnt   Score   Error  Units
StringUtilsIsMixedCaseTest.newIsMixedCaseBeginningMatch  avgt   25   4.521 ± 0.089  ns/op
StringUtilsIsMixedCaseTest.newIsMixedCaseEndMatch        avgt   25  28.746 ± 0.069  ns/op
StringUtilsIsMixedCaseTest.newIsMixedCaseMiddleMatch     avgt   25  14.143 ± 0.077  ns/op
StringUtilsIsMixedCaseTest.newIsMixedCaseNoneMatch       avgt   25  50.617 ± 0.232  ns/op
StringUtilsIsMixedCaseTest.oldIsMixedCaseBeginningMatch  avgt   25   5.655 ± 0.520  ns/op
StringUtilsIsMixedCaseTest.oldIsMixedCaseEndMatch        avgt   25  35.974 ± 0.907  ns/op
StringUtilsIsMixedCaseTest.oldIsMixedCaseMiddleMatch     avgt   25  15.270 ± 0.067  ns/op
StringUtilsIsMixedCaseTest.oldIsMixedCaseNoneMatch       avgt   25  73.331 ± 0.101  ns/op

Process finished with exit code 0

As you could see, in every senorio I tested the code is between 1ns and 25ns per op faster.

@garydgregory
Copy link
Member

Hi @hduelme
Thank you for providing benchmarks.
It looks like assuming the worst case the new code is better than the new because the score of all new test runs plus the error margin is always less than the score of the old test runs minus the error margin. Check?

Copy link
Member

@garydgregory garydgregory left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @hduelme
The new file is missing the Apache license header. See any other Java file.

@garydgregory
Copy link
Member

Also, please rebase on master so we can make sure this PR builds properly.

The changes optimize the mixed-case detecting function in StringUtils.java by reducing redundant character property checks. The loop will now return early when both uppercase and lowercase are present, preventing further unnecessary operations. It also introduces a local variable to store the character being checked, reducing the number of method calls to get the character. The end return statement is also simplified to return false, as the true condition has already been covered inside the loop.
@hduelme hduelme force-pushed the improve-performance-of-isMixedCase branch from d9ba517 to fcedff2 Compare August 15, 2023 14:14
@hduelme
Copy link
Contributor Author

hduelme commented Aug 15, 2023

@garydgregory rebased and license header added

@hduelme
Copy link
Contributor Author

hduelme commented Aug 15, 2023

Hi @hduelme Thank you for providing benchmarks. It looks like assuming the worst case the new code is better than the new because the score of all new test runs plus the error margin is always less than the score of the old test runs minus the error margin. Check?

That was also surprising for me. But yes looks like it.

@garydgregory garydgregory changed the title improve performance of isMixedCase Improve performance of StringUtils.isMixedCase() Aug 15, 2023
@codecov-commenter
Copy link

Codecov Report

Merging #1096 (0a31b33) into master (842f71d) will increase coverage by 0.00%.
Report is 2 commits behind head on master.
The diff coverage is 100.00%.

@@            Coverage Diff            @@
##             master    #1096   +/-   ##
=========================================
  Coverage     92.21%   92.21%           
+ Complexity     7551     7549    -2     
=========================================
  Files           197      197           
  Lines         15801    15802    +1     
  Branches       2923     2922    -1     
=========================================
+ Hits          14571    14572    +1     
  Misses          660      660           
  Partials        570      570           
Files Changed Coverage Δ
...ain/java/org/apache/commons/lang3/StringUtils.java 98.86% <100.00%> (+<0.01%) ⬆️

📣 We’re building smart automated test selection to slash your CI/CD build times. Learn more

@garydgregory garydgregory merged commit 19fa964 into apache:master Aug 16, 2023
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants