Exceptional Exceptions
11 June 2016
Creating an exception in Java is a very slow operation. Expect that throwing an exception will cost you around 1-5 microseconds. Nearly all this time is spent on filling in the exception thread stack. The deeper the stack trace is, the more time it will take to populate it. If you take a peek on java.lang.Throwable
source, you will see the answer staring into your face.
public Throwable() {
fillInStackTrace();
}
public synchronized Throwable fillInStackTrace() {
if (stackTrace != null ||
backtrace != null /* Out of protocol state */ ) {
fillInStackTrace(0);
stackTrace = UNASSIGNED_STACK;
}
return this;
}
private native Throwable fillInStackTrace(int dummy);
Usually we throw an exception in case of unexpected problems. This means that we don’t use exceptions as part of the the repetitive if-then-else mess.
Creating the project
$ mvn archetype:generate \
-DinteractiveMode=false \
-DarchetypeGroupId=org.openjdk.jmh \
-DarchetypeArtifactId=jmh-java-benchmark-archetype \
-DgroupId=com.prototype \
-DartifactId=benchmark \
-Dversion=1.0
Writing the benchmark
package com.prototype;
import java.util.concurrent.TimeUnit;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Warmup;
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.MICROSECONDS)
public class ExceptionBenchmark {
@Benchmark
public Object baseline() {
return new Object();
}
@Benchmark
public Object exceptional() {
return new RuntimeException();
}
}
Compiling
$ mvn clean install
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building JMH benchmark sample: Java 1.0
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ benchmark ---
[INFO] Deleting /home/drmanalo/workspace/benchmark/target
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ benchmark ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /home/drmanalo/workspace/benchmark/src/main/resources
[INFO]
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ benchmark ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to /home/drmanalo/workspace/benchmark/target/classes
[INFO]
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ benchmark ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /home/drmanalo/workspace/benchmark/src/test/resources
[INFO]
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ benchmark ---
[INFO] No sources to compile
[INFO]
[INFO] --- maven-surefire-plugin:2.17:test (default-test) @ benchmark ---
[INFO] No tests to run.
[INFO]
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ benchmark ---
[INFO] Building jar: /home/drmanalo/workspace/benchmark/target/benchmark-1.0.jar
[INFO]
[INFO] --- maven-shade-plugin:2.2:shade (default) @ benchmark ---
[INFO] Including org.openjdk.jmh:jmh-core:jar:1.12 in the shaded jar.
[INFO] Including net.sf.jopt-simple:jopt-simple:jar:4.6 in the shaded jar.
[INFO] Including org.apache.commons:commons-math3:jar:3.2 in the shaded jar.
[INFO] Replacing /home/drmanalo/workspace/benchmark/target/benchmarks.jar with /home/drmanalo/workspace/benchmark/target/benchmark-1.0-shaded.jar
[INFO]
[INFO] --- maven-install-plugin:2.5.1:install (default-install) @ benchmark ---
[INFO] Installing /home/drmanalo/workspace/benchmark/target/benchmark-1.0.jar to /home/drmanalo/.m2/repository/com/prototype/benchmark/1.0/benchmark-1.0.jar
[INFO] Installing /home/drmanalo/workspace/benchmark/pom.xml to /home/drmanalo/.m2/repository/com/prototype/benchmark/1.0/benchmark-1.0.pom
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2.604 s
[INFO] Finished at: 2016-06-21T23:03:56+01:00
[INFO] Final Memory: 24M/203M
[INFO] ------------------------------------------------------------------------
Running the benchmarks
$ java -jar target/benchmarks.jar
# JMH 1.12 (released 82 days ago)
# VM version: JDK 1.8.0_91, VM 25.91-b14
# VM invoker: /opt/jdk1.8.0_91/jre/bin/java
# VM options: <none>
# Warmup: 5 iterations, 1 us each
# Measurement: 20 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Average time, time/op
# Benchmark: com.prototype.ExceptionBenchmark.baseline
# Run progress: 0.00% complete, ETA 00:06:40
# Fork: 1 of 10
# Warmup Iteration 1: 0.243 us/op
# Warmup Iteration 2: 0.038 us/op
# Warmup Iteration 3: 0.011 us/op
# Warmup Iteration 4: 0.016 us/op
# Warmup Iteration 5: 0.004 us/op
Iteration 1: 0.004 us/op
Iteration 2: 0.004 us/op
Iteration 3: 0.004 us/op
Iteration 4: 0.004 us/op
Iteration 5: 0.004 us/op
Iteration 6: 0.004 us/op
Iteration 7: 0.004 us/op
Iteration 8: 0.004 us/op
Iteration 9: 0.004 us/op
Iteration 10: 0.004 us/op
Iteration 11: 0.004 us/op
Iteration 12: 0.004 us/op
Iteration 13: 0.004 us/op
Iteration 14: 0.004 us/op
Iteration 15: 0.004 us/op
Iteration 16: 0.004 us/op
Iteration 17: 0.004 us/op
Iteration 18: 0.004 us/op
Iteration 19: 0.004 us/op
Iteration 20: 0.004 us/op
# JMH 1.12 (released 82 days ago)
# VM version: JDK 1.8.0_91, VM 25.91-b14
# VM invoker: /opt/jdk1.8.0_91/jre/bin/java
# VM options: <none>
# Warmup: 5 iterations, 1 us each
# Measurement: 20 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Average time, time/op
# Benchmark: com.prototype.ExceptionBenchmark.exceptional
# Run progress: 50.00% complete, ETA 00:03:29
# Fork: 1 of 10
# Warmup Iteration 1: 2.083 us/op
# Warmup Iteration 2: 2.454 us/op
# Warmup Iteration 3: 1.918 us/op
# Warmup Iteration 4: 1.853 us/op
# Warmup Iteration 5: 1.455 us/op
Iteration 1: 1.096 us/op
Iteration 2: 1.067 us/op
Iteration 3: 1.073 us/op
Iteration 4: 1.075 us/op
Iteration 5: 1.069 us/op
Iteration 6: 1.074 us/op
Iteration 7: 1.075 us/op
Iteration 8: 1.065 us/op
Iteration 9: 1.074 us/op
Iteration 10: 1.070 us/op
Iteration 11: 1.075 us/op
Iteration 12: 0.994 us/op
Iteration 13: 0.991 us/op
Iteration 14: 0.994 us/op
Iteration 15: 1.003 us/op
Iteration 16: 0.999 us/op
Iteration 17: 1.006 us/op
Iteration 18: 1.001 us/op
Iteration 19: 0.999 us/op
Iteration 20: 0.998 us/op
and the test results...
Result "baseline":
0.004 ±(99.9%) 0.001 us/op [Average]
(min, avg, max) = (0.004, 0.004, 0.005), stdev = 0.001
CI (99.9%): [0.004, 0.004] (assumes normal distribution)
Result "exceptional":
1.017 ±(99.9%) 0.015 us/op [Average]
(min, avg, max) = (0.944, 1.017, 1.594), stdev = 0.062
CI (99.9%): [1.002, 1.032] (assumes normal distribution)
Benchmark Mode Cnt Score Error Units
ExceptionBenchmark.baseline avgt 200 0.004 ± 0.001 us/op
ExceptionBenchmark.exceptional avgt 200 1.017 ± 0.015 us/op