blob: 07b2dcfdc4eee1f485d190be76615eb1669ce53f [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.lang3.math;
import java.util.concurrent.TimeUnit;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;
/**
* {@link NumberUtils#createNumber(String)} See https://github.com/apache/commons-lang/pull/1628
*
* <pre>
* mvn test -P benchmark -Dbenchmark=NumberUtilsBenchmark -P '!jacoco'
* </pre>
* Results on my machine on 2026-04-26:
// @formatter:off
* <pre>
# JMH version: 1.37
# VM version: JDK 21.0.11, OpenJDK 64-Bit Server VM, 21.0.11
# VM invoker: /opt/homebrew/Cellar/openjdk@21/21.0.11/libexec/openjdk.jdk/Contents/Home/bin/java
# VM options: <none>
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
# Warmup: 3 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.math.NumberUtilsBenchmark.testDefaultCheck
# Run progress: 0.00% complete, ETA 00:08:00
# Fork: 1 of 3
# Warmup Iteration 1: 33.679 ns/op
# Warmup Iteration 2: 32.796 ns/op
# Warmup Iteration 3: 33.406 ns/op
Iteration 1: 33.474 ns/op
Iteration 2: 33.479 ns/op
Iteration 3: 33.301 ns/op
Iteration 4: 33.691 ns/op
Iteration 5: 33.523 ns/op
# Run progress: 16.67% complete, ETA 00:06:40
# Fork: 2 of 3
# Warmup Iteration 1: 33.716 ns/op
# Warmup Iteration 2: 33.370 ns/op
# Warmup Iteration 3: 33.073 ns/op
Iteration 1: 33.057 ns/op
Iteration 2: 33.076 ns/op
Iteration 3: 32.987 ns/op
Iteration 4: 33.102 ns/op
Iteration 5: 33.060 ns/op
# Run progress: 33.33% complete, ETA 00:05:20
# Fork: 3 of 3
# Warmup Iteration 1: 34.668 ns/op
# Warmup Iteration 2: 33.436 ns/op
# Warmup Iteration 3: 33.182 ns/op
Iteration 1: 33.022 ns/op
Iteration 2: 33.164 ns/op
Iteration 3: 33.171 ns/op
Iteration 4: 33.050 ns/op
Iteration 5: 33.126 ns/op
Result "org.apache.commons.lang3.math.NumberUtilsBenchmark.testDefaultCheck":
33.219 ±(99.9%) 0.235 ns/op [Average]
(min, avg, max) = (32.987, 33.219, 33.691), stdev = 0.220
CI (99.9%): [32.984, 33.454] (assumes normal distribution)
# JMH version: 1.37
# VM version: JDK 21.0.11, OpenJDK 64-Bit Server VM, 21.0.11
# VM invoker: /opt/homebrew/Cellar/openjdk@21/21.0.11/libexec/openjdk.jdk/Contents/Home/bin/java
# VM options: <none>
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
# Warmup: 3 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.math.NumberUtilsBenchmark.testShortcircuitCheck
# Run progress: 50.00% complete, ETA 00:04:00
# Fork: 1 of 3
# Warmup Iteration 1: 0.587 ns/op
# Warmup Iteration 2: 0.586 ns/op
# Warmup Iteration 3: 0.586 ns/op
Iteration 1: 0.586 ns/op
Iteration 2: 0.586 ns/op
Iteration 3: 0.586 ns/op
Iteration 4: 0.586 ns/op
Iteration 5: 0.586 ns/op
# Run progress: 66.67% complete, ETA 00:02:40
# Fork: 2 of 3
# Warmup Iteration 1: 0.627 ns/op
# Warmup Iteration 2: 0.586 ns/op
# Warmup Iteration 3: 0.587 ns/op
Iteration 1: 0.586 ns/op
Iteration 2: 0.629 ns/op
Iteration 3: 0.586 ns/op
Iteration 4: 0.585 ns/op
Iteration 5: 0.587 ns/op
# Run progress: 83.33% complete, ETA 00:01:20
# Fork: 3 of 3
# Warmup Iteration 1: 0.628 ns/op
# Warmup Iteration 2: 0.586 ns/op
# Warmup Iteration 3: 0.587 ns/op
Iteration 1: 0.587 ns/op
Iteration 2: 0.587 ns/op
Iteration 3: 0.621 ns/op
Iteration 4: 0.624 ns/op
Iteration 5: 0.587 ns/op
Result "org.apache.commons.lang3.math.NumberUtilsBenchmark.testShortcircuitCheck":
0.594 ±(99.9%) 0.017 ns/op [Average]
(min, avg, max) = (0.585, 0.594, 0.629), stdev = 0.016
CI (99.9%): [0.577, 0.611] (assumes normal distribution)
# Run complete. Total time: 00:08:01
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.
NOTE: Current JVM experimentally supports Compiler Blackholes, and they are in use. Please exercise
extra caution when trusting the results, look into the generated code to check the benchmark still
works, and factor in a small probability of new VM bugs. Additionally, while comparisons between
different JVMs are already problematic, the performance difference caused by different Blackhole
modes can be very significant. Please make sure you use the consistent Blackhole mode for comparisons.
Benchmark Mode Cnt Score Error Units
NumberUtilsBenchmark.testDefaultCheck avgt 15 33.219 ± 0.235 ns/op
NumberUtilsBenchmark.testShortcircuitCheck avgt 15 0.594 ± 0.017 ns/op
Benchmark result is saved to target/jmh-result.NumberUtilsBenchmark.json
* </pre>
// @formatter:on
*/
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Thread)
@Fork(3)
@Warmup(iterations = 3)
@Measurement(iterations = 5)
public class NumberUtilsBenchmark {
private static boolean isAllZeros(final String str) {
if (str == null) {
return true;
}
for (int i = str.length() - 1; i >= 0; i--) {
if (str.charAt(i) != '0') {
return false;
}
}
return true;
}
private static boolean isZero(final String mant, final String dec) {
return isAllZeros(mant) && isAllZeros(dec);
}
private final String str = "0.25";
private final String mant = "0";
private final String dec = "25";
Float f = Float.valueOf(str);
Double d = Double.valueOf(str);
@Benchmark
public boolean testDefaultCheck() {
return !f.isInfinite() && !(f.floatValue() == 0.0F && !isZero(mant, dec)) && f.toString().equals(d.toString());
}
@Benchmark
public boolean testShortcircuitCheck() {
return !f.isInfinite() && !(f.floatValue() == 0.0F && !isZero(mant, dec)) && (d.floatValue() == d.doubleValue() || f.toString().equals(d.toString()));
}
}