On Wed, 27 Jul 2022 22:56:39 GMT, David Schlosnagle <d...@openjdk.org> wrote:

> I would like to contribute an optimized version of 
> `StackTraceElement#toString()` that uses a single StringBuilder throughout 
> creation to avoid intermediate `String` allocations. 
> `StackTraceElement#toString()` is used in a number of JDK code paths 
> including `Throwable#printStackTrace()`, as well as many JDK consumers may 
> transform `StackTraceElement` `toString()` in logging frameworks capturing 
> throwables and exceptions, and diagnostics performing dumps.
> 
> Given this usage and some observed JFR profiles from production services, I'd 
> like to reduce the intermediate allocations to reduce CPU pressure in these 
> circumstances. I have added a couple benchmarks for a sample 
> `Throwable#printStackTrace()` converted to String via `StringWriter` and 
> individual `StackTraceElement` `toString`. The former shows ~15% improvement, 
> while the latter shows ~40% improvement.
> 
> Before
> 
> Benchmark                               Mode  Cnt       Score      Error  
> Units
> StackTraceElementBench.printStackTrace  avgt   15  167147.066 ± 4260.521  
> ns/op
> StackTraceElementBench.toString         avgt   15     132.781 ±    2.095  
> ns/op
> 
> 
> After
> 
> Benchmark                               Mode  Cnt       Score      Error  
> Units
> StackTraceElementBench.printStackTrace  avgt   15  142909.133 ± 2290.720  
> ns/op
> StackTraceElementBench.toString         avgt   15      78.939 ±    0.469  
> ns/op

I think there's another angle to consider here:
This wasn't an issue using java 8 and earlier because the data encapsulated by 
StackTraceElement was available using the public API, this meant that logging 
frameworks were free to implement custom formatters that could be used in place 
of the toString implementation without losing features and optimizing for niche 
use-cases (e.g. allocation avoidance).
In recent java versions, the StackTraceElement string value is based on data 
that's not available using the public API (whether the classloader is builtin, 
and whether the module is from java.base). String matching can get around this 
at a potential correctness cost.

It would be helpful for my uses if the new internal attributes were exposed in 
some way from StackTraceElement, however I understand why that isn't desirable 
from an API design standpoint.
I suspect a public `appendTo(Appendable)` is a more feasible API addition as it 
encapsulates the functionality without awareness of what a builtin classloader 
is, and roughly matches prior art within `Throwable.printStackTrace`. I'd love 
to get an opinion from someone more closely involved with openjdk core-libs 
development :-)

src/java.base/share/classes/java/lang/StackTraceElement.java line 374:

> 372:      * @throws IOException If an I/O error occurs
> 373:      */
> 374:     private void appendTo(Appendable dest) throws IOException {

Perhaps this could be package-private for reuse by `Throwable.printStackTrace`, 
avoiding the intermediate toString.

-------------

PR: https://git.openjdk.org/jdk/pull/9665

Reply via email to