Hi David, I modified quickly your project to use jmh (more by laziness than anything else), made it sequential (important point is not the possible load on my machine by itself but the comparative self duration of the execution). Your stack setup (20, 5 - ie ~120 stack elements accross 4-5 stacked execptions is really good cause representative of what we have in EE apps) so I kept it and just added one with 20,4 to have an informative comparison point. I also added jsonb (de)serialization and snippet items to be able to compare.
Here is the code: /* * 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 * * http://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.example; import org.apache.johnzon.core.Snippet; import org.openjdk.jmh.Main; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.Fork; import org.openjdk.jmh.annotations.Measurement; import org.openjdk.jmh.annotations.Scope; import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.annotations.Warmup; import org.openjdk.jmh.infra.Blackhole; import javax.json.Json; import javax.json.JsonObject; import javax.json.bind.Jsonb; import javax.json.bind.JsonbBuilder; import java.io.OutputStream; import java.io.PrintStream; import static java.util.Collections.emptyMap; @State(Scope.Benchmark) public class SimpleRunner { private final Orange orange_20_5 = new Orange(20, 5); // represents a common EE stack (>=120 lines commonly with ~4 causes) private final Orange orange_20_4 = new Orange(20, 4); private final Color color = new Color(); private final JsonObject jsonColor = Json.createObjectBuilder() .add("r", 255) .add("g", 255) .add("b", 255) .build(); private final String stringColor = jsonColor.toString(); // yes i'm lazy private final Jsonb jsonb = JsonbBuilder.create(); private final Snippet snippet = new Snippet(50, Json.createGeneratorFactory(emptyMap())); @Benchmark @Fork(1) @Warmup(iterations = 2, time = 10) @Measurement(iterations = 2, time = 10) public void jsonbToJson(final Blackhole blackhole) { blackhole.consume(jsonb.toJson(color)); } @Benchmark @Fork(1) @Warmup(iterations = 5, time = 10) @Measurement(iterations = 5, time = 10) public void jsonbFromJson(final Blackhole blackhole) { blackhole.consume(jsonb.fromJson(stringColor, Color.class)); } @Benchmark @Fork(1) @Warmup(iterations = 5, time = 10) @Measurement(iterations = 5, time = 10) public void snippet(final Blackhole blackhole) { blackhole.consume(snippet.of(jsonColor)); } @Benchmark @Fork(1) // no need of a ton of iterations, peek is quickly reached @Warmup(iterations = 2, time = 10) @Measurement(iterations = 2, time = 10) public void run_20_5(final Blackhole blackhole) { try { blackhole.consume(orange_20_5.method(100)); } catch (final OrangeException oe) { print(oe); } } @Benchmark @Fork(1) // no need of a ton of iterations, peek is quickly reached @Warmup(iterations = 2, time = 10) @Measurement(iterations = 2, time = 10) public void run_20_4(final Blackhole blackhole) { try { blackhole.consume(orange_20_4.method(100)); } catch (final OrangeException oe) { print(oe); } } private void print(final OrangeException oe) { oe.printStackTrace(new PrintStream(new OutputStream() { @Override public void write(final int b) { // no-op } @Override public void write(final byte[] b) { // no-op } @Override public void write(final byte[] b, final int off, final int len) { // no-op } }) { @Override public void println(final String x) { // no-op } }); } public static void main(final String... args) throws Exception { System.out.println(); System.out.println("Java Version: " + System.getProperty("java.version")); // just to check the run script setup using sdk System.out.println(); Main.main(args); } public static class Color { public int r = 255; public int g = 0; public int b = 0; } } Note: indeed the JSON model is quite simple so JSON-B round trips are fast but overall it shows the impact of exceptions over JSON-B (feel free to play with a medium and big size models to compare if you need). I ran it on java 8 (zulu/openjdk + oracle), 11 (zulu only), 17 (zulu + oracle), here are the results: *Vendor* *Java Version* *Scenario* *op/s* *Azul (zulu)* *8.0.282* *jsonbFromJson* 1 127 870,58 *Azul (zulu)* *8.0.282* *jsonbToJson* 1 224 234,98 *Azul (zulu)* *8.0.282* *snippet* 1 854 636,08 *Azul (zulu)* *8.0.282* *run_20_5* 11 712,18 *Azul (zulu)* *8.0.282* *run_20_4* 11 410,87 *Azul (zulu)* *8.0.282* *run_20_1* 11 922,60 *Oracle* *8.0.333* *jsonbFromJson* 1 119 471,92 *Oracle* *8.0.333* *jsonbToJson* 1 234 185,99 *Oracle* *8.0.333* *snippet* 1 894 505,32 *Oracle* *8.0.333* *run_20_5* 11 859,83 *Oracle* *8.0.333* *run_20_4* 12 300,20 *Oracle* *8.0.333* *run_20_1* 12 168,84 *Azul (zulu)* *11.0.14* *jsonbFromJson* 1 058 215,96 *Azul (zulu)* *11.0.14* *jsonbToJson* 1 363 395,61 *Azul (zulu)* *11.0.14* *snippet* 2 039 566,47 *Azul (zulu)* *11.0.14* *run_20_5* 16 866,54 *Azul (zulu)* *11.0.14* *run_20_4* 16 396,28 *Azul (zulu)* *11.0.14* *run_20_1* 16 751,55 *Azul (zulu)* *17.0.3* *jsonbFromJson* 1 063 599,18 *Azul (zulu)* *17.0.3* *jsonbToJson* 1 222 513,14 *Azul (zulu)* *17.0.3* *snippet* 2 131 294,24 *Azul (zulu)* *17.0.3* *run_20_5* 14 377,94 *Azul (zulu)* *17.0.3* *run_20_4* 14 359,60 *Azul (zulu)* *17.0.3* *run_20_1* 14 129,01 *Oracle* *17.0.3* *jsonbFromJson* 1 084 722,60 *Oracle* *17.0.3* *jsonbToJson* 1 396 424,78 *Oracle* *17.0.3* *snippet* 2 086 953,45 *Oracle* *17.0.3* *run_20_5* 14 199,80 *Oracle* *17.0.3* *run_20_4* 14 189,93 *Oracle* *17.0.3* *run_20_1* 14 886,94 What we can conclude is that it reinforces the fact exceptions are a performance killer if we don't handle them specifically (more after) but that having 4-5 or 6 stacked exception does not dramatically change the overall performances - and all that is shared by a good set of JVM so no "luck". So overall on the fact that we can have 4 or 5 exceptions I join you it will not change the overall behavior in terms of performances so it leads to 2 question from my side: 1. what do we want to expose in terms of API regarding exceptions? How an user can get backs its business exception from an API standpoint? I guess having a constant unwrapping woud be good compared to having to visit all exceptions to filter them - at least to not require users to handle "looping" exception, ie getCause==this - but not sure how needed it is. 2. since 1.3 was pushed due to the changes in exceptions, do we want to enhance these performances, optionally by precomputing stacktraces and their rendering of several exceptions and forcing them instead of calculating the stacktraces and their rendering (a bit like netty does) I don't have yet a strong opinion on both, I think both would be good and could justify even a bit more the 1.3 change but I'm not sure the real impact in applications. Romain Manni-Bucau @rmannibucau <https://twitter.com/rmannibucau> | Blog <https://rmannibucau.metawerx.net/> | Old Blog <http://rmannibucau.wordpress.com> | Github <https://github.com/rmannibucau> | LinkedIn <https://www.linkedin.com/in/rmannibucau> | Book <https://www.packtpub.com/application-development/java-ee-8-high-performance> Le mar. 17 mai 2022 à 23:18, David Blevins <[email protected]> a écrit : > > On May 16, 2022, at 11:31 PM, Romain Manni-Bucau <[email protected]> > wrote: > > > > Hi David, can you share it? Do you have stacktrace of > 200 elements with > > 4-5 exceptions linked? > > Did you test on all jvm (oracle, zulu, java 8-17)? > > No, I wasn't that curious. Here's the code if you want to play: > > - https://github.com/dblevins/exception-testing > > The code is ugly and I'm sure flawed. The jvm I used was temurin-16.jdk > > One thought I had is that the 100-deep or 200-deep stack trace situations > are most likely to occur when the walking over a large document. In that > situation the size of the stack trace is still going to be a rounding error > compared to the size of the document in memory. > > > -David > >
