Skip to content

Commit 45db284

Browse files
committed
Add NumberUtilsBenchmark
See #1628
1 parent a4a090a commit 45db284

1 file changed

Lines changed: 210 additions & 0 deletions

File tree

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* https://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.commons.lang3.math;
19+
20+
import java.util.concurrent.TimeUnit;
21+
22+
import org.openjdk.jmh.annotations.Benchmark;
23+
import org.openjdk.jmh.annotations.BenchmarkMode;
24+
import org.openjdk.jmh.annotations.Fork;
25+
import org.openjdk.jmh.annotations.Measurement;
26+
import org.openjdk.jmh.annotations.Mode;
27+
import org.openjdk.jmh.annotations.OutputTimeUnit;
28+
import org.openjdk.jmh.annotations.Scope;
29+
import org.openjdk.jmh.annotations.State;
30+
import org.openjdk.jmh.annotations.Warmup;
31+
32+
/**
33+
* {@link NumberUtils#createNumber(String)} See https://github.com/apache/commons-lang/pull/1628
34+
*
35+
* <pre>
36+
* mvn test -P benchmark -Dbenchmark=NumberUtilsBenchmark -P '!jacoco'
37+
* </pre>
38+
* Results on my machine on 2026-04-26:
39+
// @formatter:off
40+
* <pre>
41+
# JMH version: 1.37
42+
# VM version: JDK 21.0.11, OpenJDK 64-Bit Server VM, 21.0.11
43+
# VM invoker: /opt/homebrew/Cellar/openjdk@21/21.0.11/libexec/openjdk.jdk/Contents/Home/bin/java
44+
# VM options: <none>
45+
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
46+
# Warmup: 3 iterations, 10 s each
47+
# Measurement: 5 iterations, 10 s each
48+
# Timeout: 10 min per iteration
49+
# Threads: 1 thread, will synchronize iterations
50+
# Benchmark mode: Average time, time/op
51+
# Benchmark: org.apache.commons.lang3.math.NumberUtilsBenchmark.testDefaultCheck
52+
53+
# Run progress: 0.00% complete, ETA 00:08:00
54+
# Fork: 1 of 3
55+
# Warmup Iteration 1: 33.679 ns/op
56+
# Warmup Iteration 2: 32.796 ns/op
57+
# Warmup Iteration 3: 33.406 ns/op
58+
Iteration 1: 33.474 ns/op
59+
Iteration 2: 33.479 ns/op
60+
Iteration 3: 33.301 ns/op
61+
Iteration 4: 33.691 ns/op
62+
Iteration 5: 33.523 ns/op
63+
64+
# Run progress: 16.67% complete, ETA 00:06:40
65+
# Fork: 2 of 3
66+
# Warmup Iteration 1: 33.716 ns/op
67+
# Warmup Iteration 2: 33.370 ns/op
68+
# Warmup Iteration 3: 33.073 ns/op
69+
Iteration 1: 33.057 ns/op
70+
Iteration 2: 33.076 ns/op
71+
Iteration 3: 32.987 ns/op
72+
Iteration 4: 33.102 ns/op
73+
Iteration 5: 33.060 ns/op
74+
75+
# Run progress: 33.33% complete, ETA 00:05:20
76+
# Fork: 3 of 3
77+
# Warmup Iteration 1: 34.668 ns/op
78+
# Warmup Iteration 2: 33.436 ns/op
79+
# Warmup Iteration 3: 33.182 ns/op
80+
Iteration 1: 33.022 ns/op
81+
Iteration 2: 33.164 ns/op
82+
Iteration 3: 33.171 ns/op
83+
Iteration 4: 33.050 ns/op
84+
Iteration 5: 33.126 ns/op
85+
86+
87+
Result "org.apache.commons.lang3.math.NumberUtilsBenchmark.testDefaultCheck":
88+
33.219 ±(99.9%) 0.235 ns/op [Average]
89+
(min, avg, max) = (32.987, 33.219, 33.691), stdev = 0.220
90+
CI (99.9%): [32.984, 33.454] (assumes normal distribution)
91+
92+
93+
# JMH version: 1.37
94+
# VM version: JDK 21.0.11, OpenJDK 64-Bit Server VM, 21.0.11
95+
# VM invoker: /opt/homebrew/Cellar/openjdk@21/21.0.11/libexec/openjdk.jdk/Contents/Home/bin/java
96+
# VM options: <none>
97+
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
98+
# Warmup: 3 iterations, 10 s each
99+
# Measurement: 5 iterations, 10 s each
100+
# Timeout: 10 min per iteration
101+
# Threads: 1 thread, will synchronize iterations
102+
# Benchmark mode: Average time, time/op
103+
# Benchmark: org.apache.commons.lang3.math.NumberUtilsBenchmark.testShortcircuitCheck
104+
105+
# Run progress: 50.00% complete, ETA 00:04:00
106+
# Fork: 1 of 3
107+
# Warmup Iteration 1: 0.587 ns/op
108+
# Warmup Iteration 2: 0.586 ns/op
109+
# Warmup Iteration 3: 0.586 ns/op
110+
Iteration 1: 0.586 ns/op
111+
Iteration 2: 0.586 ns/op
112+
Iteration 3: 0.586 ns/op
113+
Iteration 4: 0.586 ns/op
114+
Iteration 5: 0.586 ns/op
115+
116+
# Run progress: 66.67% complete, ETA 00:02:40
117+
# Fork: 2 of 3
118+
# Warmup Iteration 1: 0.627 ns/op
119+
# Warmup Iteration 2: 0.586 ns/op
120+
# Warmup Iteration 3: 0.587 ns/op
121+
Iteration 1: 0.586 ns/op
122+
Iteration 2: 0.629 ns/op
123+
Iteration 3: 0.586 ns/op
124+
Iteration 4: 0.585 ns/op
125+
Iteration 5: 0.587 ns/op
126+
127+
# Run progress: 83.33% complete, ETA 00:01:20
128+
# Fork: 3 of 3
129+
# Warmup Iteration 1: 0.628 ns/op
130+
# Warmup Iteration 2: 0.586 ns/op
131+
# Warmup Iteration 3: 0.587 ns/op
132+
Iteration 1: 0.587 ns/op
133+
Iteration 2: 0.587 ns/op
134+
Iteration 3: 0.621 ns/op
135+
Iteration 4: 0.624 ns/op
136+
Iteration 5: 0.587 ns/op
137+
138+
139+
Result "org.apache.commons.lang3.math.NumberUtilsBenchmark.testShortcircuitCheck":
140+
0.594 ±(99.9%) 0.017 ns/op [Average]
141+
(min, avg, max) = (0.585, 0.594, 0.629), stdev = 0.016
142+
CI (99.9%): [0.577, 0.611] (assumes normal distribution)
143+
144+
145+
# Run complete. Total time: 00:08:01
146+
147+
REMEMBER: The numbers below are just data. To gain reusable insights, you need to follow up on
148+
why the numbers are the way they are. Use profilers (see -prof, -lprof), design factorial
149+
experiments, perform baseline and negative tests that provide experimental control, make sure
150+
the benchmarking environment is safe on JVM/OS/HW level, ask for reviews from the domain experts.
151+
Do not assume the numbers tell you what you want them to tell.
152+
153+
NOTE: Current JVM experimentally supports Compiler Blackholes, and they are in use. Please exercise
154+
extra caution when trusting the results, look into the generated code to check the benchmark still
155+
works, and factor in a small probability of new VM bugs. Additionally, while comparisons between
156+
different JVMs are already problematic, the performance difference caused by different Blackhole
157+
modes can be very significant. Please make sure you use the consistent Blackhole mode for comparisons.
158+
159+
Benchmark Mode Cnt Score Error Units
160+
NumberUtilsBenchmark.testDefaultCheck avgt 15 33.219 ± 0.235 ns/op
161+
NumberUtilsBenchmark.testShortcircuitCheck avgt 15 0.594 ± 0.017 ns/op
162+
163+
Benchmark result is saved to target/jmh-result.NumberUtilsBenchmark.json
164+
* </pre>
165+
// @formatter:on
166+
*/
167+
@BenchmarkMode(Mode.AverageTime)
168+
@OutputTimeUnit(TimeUnit.NANOSECONDS)
169+
@State(Scope.Thread)
170+
@Fork(3)
171+
@Warmup(iterations = 3)
172+
@Measurement(iterations = 5)
173+
public class NumberUtilsBenchmark {
174+
175+
private static boolean isAllZeros(final String str) {
176+
if (str == null) {
177+
return true;
178+
}
179+
for (int i = str.length() - 1; i >= 0; i--) {
180+
if (str.charAt(i) != '0') {
181+
return false;
182+
}
183+
}
184+
return true;
185+
}
186+
187+
private static boolean isZero(final String mant, final String dec) {
188+
return isAllZeros(mant) && isAllZeros(dec);
189+
}
190+
191+
private final String str = "0.25";
192+
193+
private final String mant = "0";
194+
195+
private final String dec = "25";
196+
197+
Float f = Float.valueOf(str);
198+
199+
Double d = Double.valueOf(str);
200+
201+
@Benchmark
202+
public boolean testDefaultCheck() {
203+
return !f.isInfinite() && !(f.floatValue() == 0.0F && !isZero(mant, dec)) && f.toString().equals(d.toString());
204+
}
205+
206+
@Benchmark
207+
public boolean testShortcircuitCheck() {
208+
return !f.isInfinite() && !(f.floatValue() == 0.0F && !isZero(mant, dec)) && (d.floatValue() == d.doubleValue() || f.toString().equals(d.toString()));
209+
}
210+
}

0 commit comments

Comments
 (0)