Path: blob/master/test/micro/org/openjdk/bench/java/lang/StackWalkBench.java
41161 views
/*1* Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/22package org.openjdk.bench.java.lang;2324import java.lang.StackWalker.StackFrame;25import java.util.concurrent.TimeUnit;26import org.openjdk.jmh.annotations.Benchmark;27import org.openjdk.jmh.annotations.BenchmarkMode;28import org.openjdk.jmh.annotations.Mode;29import org.openjdk.jmh.annotations.OutputTimeUnit;30import org.openjdk.jmh.annotations.Param;31import org.openjdk.jmh.annotations.Scope;32import org.openjdk.jmh.annotations.State;33import org.openjdk.jmh.infra.Blackhole;3435/**36* Benchmarks for java.lang.StackWalker37*/38@State(value=Scope.Benchmark)39@BenchmarkMode(Mode.AverageTime)40@OutputTimeUnit(TimeUnit.NANOSECONDS)41public class StackWalkBench {42private static final StackWalker WALKER_DEFAULT = StackWalker.getInstance();4344private static final StackWalker WALKER_CLASS =45StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);4647// TestStack will add this number of calls to the call stack48@Param({"4", "100", "1000"})49// For more thorough testing, consider:50// @Param({"4", "10", "100", "256", "1000"})51public int depth;5253// Only used by swFilterCallerClass, to specify (roughly) how far back the54// call stack the target class will be found. Not needed by other55// benchmarks, so not a @Param by default.56// @Param({"4"})57public int mark = 4;5859/** Build a call stack of a given size, then run trigger code in it.60* (Does not account for existing frames higher up in the JMH machinery).61*/62public static class TestStack {63final long fence;64long current;65final Runnable trigger;6667public TestStack(long max, Runnable trigger) {68this.fence = max;69this.current = 0;70this.trigger = trigger;71}7273public void start() {74one();75}7677public void one() {78if (check()) {79two();80}81}8283void two() {84if (check()) {85three();86}87}8889private void three() {90if (check()) {91one();92}93}9495boolean check() {96if (++current == fence) {97trigger.run();98return false;99} else {100return true;101}102}103}104105/* Class to look for when testing filtering */106static class TestMarker {107public void call(MarkedTestStack test) {108test.marked();109}110}111112/** Call stack to test filtering.113* TestMarker will make a call on the stack.114*/115static class MarkedTestStack extends TestStack {116long mark;117118/**119* @param mark How far back the stack should the TestMarker be found?120*/121public MarkedTestStack(long max, long mark, Runnable trigger) {122super(max, trigger);123if (mark > max) {124throw new IllegalArgumentException("mark must be <= max");125}126this.mark = max - mark; // Count backwards from the completed call stack127}128@Override129public void start() {130if (mark == 0) {131mark();132} else {133super.one();134}135}136@Override137boolean check() {138if (++current == mark) {139mark();140return false;141} else if (current == fence) {142trigger.run();143return false;144} else {145return true;146}147}148void mark() {149new TestMarker().call(this);150}151public void marked() {152if (current < fence) {153if (check()) {154one();155}156} else {157trigger.run();158}159}160}161162/**163* StackWalker.forEach() with default options164*/165@Benchmark166public void forEach_DefaultOpts(Blackhole bh) {167final Blackhole localBH = bh;168final boolean[] done = {false};169new TestStack(depth, new Runnable() {170public void run() {171WALKER_DEFAULT.forEach(localBH::consume);172done[0] = true;173}174}).start();175if (!done[0]) {176throw new RuntimeException();177}178}179180/**181* Use Stackwalker.walk() to fetch class names182*/183@Benchmark184public void walk_ClassNames(Blackhole bh) {185final Blackhole localBH = bh;186final boolean[] done = {false};187new TestStack(depth, new Runnable() {188public void run() {189WALKER_DEFAULT.walk(s -> {190s.map(StackFrame::getClassName).forEach(localBH::consume);191return null;192});193done[0] = true;194}195}).start();196if (!done[0]) {197throw new RuntimeException();198}199}200201/**202* Use Stackwalker.walk() to fetch method names203*/204@Benchmark205public void walk_MethodNames(Blackhole bh) {206final Blackhole localBH = bh;207final boolean[] done = {false};208new TestStack(depth, new Runnable() {209public void run() {210WALKER_DEFAULT.walk( s -> {211s.map(StackFrame::getMethodName).forEach(localBH::consume);212return null;213});214done[0] = true;215}216}).start();217if (!done[0]) {218throw new RuntimeException();219}220}221222/**223* Use Stackwalker.walk() to fetch declaring class instances224*/225@Benchmark226public void walk_DeclaringClass(Blackhole bh) {227final Blackhole localBH = bh;228final boolean[] done = {false};229new TestStack(depth, new Runnable() {230public void run() {231WALKER_CLASS.walk(s -> {232s.map(StackFrame::getDeclaringClass).forEach(localBH::consume);233return null;234});235done[0] = true;236}237}).start();238if (!done[0]) {239throw new RuntimeException();240}241}242243/**244* Use StackWalker.walk() to fetch StackTraceElements245*/246@Benchmark247public void walk_StackTraceElements(Blackhole bh) {248final Blackhole localBH = bh;249final boolean[] done = {false};250new TestStack(depth, new Runnable() {251public void run() {252WALKER_DEFAULT.walk(s -> {253s.map(StackFrame::toStackTraceElement).forEach(localBH::consume);254return null;255});256done[0] = true;257}258}).start();259if (!done[0]) {260throw new RuntimeException();261}262}263264/**265* StackWalker.getCallerClass()266*/267@Benchmark268public void getCallerClass(Blackhole bh) {269final Blackhole localBH = bh;270final boolean[] done = {false};271new TestStack(depth, new Runnable() {272public void run() {273localBH.consume(WALKER_CLASS.getCallerClass());274done[0] = true;275}276}).start();277if (!done[0]) {278throw new RuntimeException();279}280}281282/**283* Use StackWalker.walk() to filter the StackFrames, looking for the284* TestMarker class, which will be (approximately) 'mark' calls back up the285* call stack.286*/287@Benchmark288public void walk_filterCallerClass(Blackhole bh) {289final Blackhole localBH = bh;290final boolean[] done = {false};291292new MarkedTestStack(depth, mark, new Runnable() {293public void run() {294// To be comparable with Reflection.getCallerClass(), return the Class object295WALKER_CLASS.walk(s -> {296localBH.consume(s.filter(f -> TestMarker.class.equals(f.getDeclaringClass())).findFirst().get().getDeclaringClass());297return null;298});299done[0] = true;300}301}).start();302303if (!done[0]) {304throw new RuntimeException();305}306}307308/**309* Use StackWalker.walk() to filter the StackFrames, looking for the310* TestMarker class, which will be (approximately) depth/2 calls back up the311* call stack.312*/313@Benchmark314public void walk_filterCallerClassHalfStack(Blackhole bh) {315final Blackhole localBH = bh;316final boolean[] done = {false};317318new MarkedTestStack(depth, depth / 2, new Runnable() {319public void run() {320// To be comparable with Reflection.getCallerClass(), return the Class object321WALKER_CLASS.walk(s -> {322localBH.consume(s.filter((f) -> TestMarker.class.equals(f.getDeclaringClass())).findFirst().get().getDeclaringClass());323return null;324});325done[0] = true;326}327}).start();328329if (!done[0]) {330throw new RuntimeException();331}332}333334// TODO: add swConsumeFramesWithReflection335// TODO: add swFilterOutStreamClasses336337// // This benchmark is for collecting performance counter data338// static PerfCounter streamTime = PerfCounter.newPerfCounter("jdk.stackwalk.testStreamsElapsedTime");339// static PerfCounter numStream = PerfCounter.newPerfCounter("jdk.stackwalk.numTestStreams");340// // @Benchmark341// public void swStkFrmsTimed(Blackhole bh) {342// final Blackhole localBH = bh;343// final boolean[] done = {false};344// new TestStack(depth, new Runnable() {345// public void run() {346// long t0 = System.nanoTime();347// WALKER_DEFAULT.forEach(localBH::consume);348// streamTime.addElapsedTimeFrom(t0);349// numStream.increment();350// done[0] = true;351// }352// }).start();353// if (!done[0]) {354// throw new RuntimeException();355// }356// }357}358359360