/*
 * @author Ranides Atterwim {@literal <ranides@gmail.com>}
 * @copyright Ranides Atterwim
 * @license WTFPL
 * @url http://ranides.net/projects/assira
 */
package net.ranides.assira.trace;

import java.io.IOException;
import java.util.Arrays;
import java.util.List;

import net.ranides.benchmark.app.BenchmarkName;
import net.ranides.benchmark.app.BenchmarkRunner;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.runner.RunnerException;

/**
 * Benchmark for different methods of obtaining list of caller-classess.
 * 
 * SecurityManager creates list ~15 times faster than stacktrace.
 * SecurityManager gets caller ~2 times faster than stacktrace.
 * Best if you need all classes.
 * It's portable, but it won't work if you disable SecurityManager in JVM
 * 
 * 
 * SunReflection creates list ~3 times faster that stacktrace.
 * SunReflection gets caller ~10 times faster than stacktrace.
 * Best if you need first class.
 * It's nonportable, but it will work even if you disable SecurityManager.
 *
 * SecurityManager gets callee/caller ~2 times faster than JDK11 StackWalker.
 * SecurityManager creates list ~5 times faster than JDK11 StackWalker.
 *
 */
public class Walker8Benchmark {

    /*
                              JDK8            JDK11
    calleeSecurityManager     1439 ±   654     1254 ±   475
    calleeException           2742 ±   942        x
    calleeThread             17189 ±   729    36706 ±  3740

    callerSecurityManager     1439 ±   849     1242 ±   344
    callerException           3329 ±  1189        x
    callerThread             37680 ± 22440    50715 ± 28560

    frameNative               3173 ±   625        x
    frameDefault             17181 ±  4192    34634 ±  4031

    frameListNative          14383 ±   720        x
    frameListDefault         17003 ±  4981    33575 ±  1432

    listSecurityManager        951 ±    62     1774 ±  1251
    listException            16285 ±   830    40529 ± 17486
    listThread               16937 ±   775    43467 ±  8709

    Walker11 JDK11
    callee                    2648 ±  1012      1254  calleeSecurityManager
    caller                    2366 ±   150      1242  callerSecurityManager
    frameNative               6391 ±   838
    frameListNative          33579 ±  1135
    list                      6623 ±   242       951  listSecurityManager

    FStackWalker11
    callee                    1420 ±  1119      OK (because we use old security manager)
    caller                    1060 ±    38      OK (because we use old security manager)
    frameNative               6391 ±   838      JDK8 was 2x faster
    frameListNative          33579 ±  1135      JDK8 was 2x faster
    list                      1482 ±   444      OK (because we use old security manager)
     */
    
    @Benchmark
    @BenchmarkName("frame[1]: native")
    public int frameNative() {
        return TraceUtils.getFrame(1).getLineNumber();
    }
    
    
    @Benchmark
    @BenchmarkName("frame[1]: default")
    public int frameDefault() {
        return getFramesArray().get(1).getLineNumber();
    }
    
    @Benchmark
    @BenchmarkName("frames: native")
    public int frameListNative() {
        int ret = 0;
        for(StackTraceElement e : TraceUtils.getFrames()) {
            ret += e.getLineNumber();
        }
        return ret;
    }
    
    @Benchmark
    @BenchmarkName("frames: default")
    public int frameListDefault() {
        int ret = 0;
        for(StackTraceElement e : getFramesArray()) {
            ret += e.getLineNumber();
        }
        return ret;
    }
    
    @Benchmark
    @BenchmarkName("names: SecurityManager")
    public int listSecurityManager() {
        return FStackWalker8.test_getNames(FStackWalker8.RESOLVER_SECURITY).size();
    }
    
    @Benchmark
    @BenchmarkName("names: thread")
    public int listThread() {
        return FStackWalker8.test_getNames(FStackWalker8.RESOLVER_THREAD).size();
    }

    @Benchmark
    @BenchmarkName("names: exception")
    public int listException() {
        return FStackWalker8.test_getNames(FStackWalker8.RESOLVER_EXCEPT).size();
    }
    
    @Benchmark
    @BenchmarkName("callee: SecurityManager")
    public int calleeSecurityManager() {
        return FStackWalker8.test_getCalleeName(FStackWalker8.RESOLVER_SECURITY).length();
    }
    
    @Benchmark
    @BenchmarkName("callee: thread")
    public int calleeThread() {
        return FStackWalker8.test_getCalleeName(FStackWalker8.RESOLVER_THREAD).length();
    }

    @Benchmark
    @BenchmarkName("callee: exception")
    public int calleeException() {
        return FStackWalker8.test_getCalleeName(FStackWalker8.RESOLVER_EXCEPT).length();
    }
    
    @Benchmark
    @BenchmarkName("caller: SecurityManager")
    public int callerSecurityManager() {
        return FStackWalker8.test_getCallerName(FStackWalker8.RESOLVER_SECURITY).length();
    }
    
    @Benchmark
    @BenchmarkName("caller: thread")
    public int callerThread() {
        return FStackWalker8.test_getCallerName(FStackWalker8.RESOLVER_THREAD).length();
    }

    @Benchmark
    @BenchmarkName("caller: exception")
    public int callerException() {
        return FStackWalker8.test_getCallerName(FStackWalker8.RESOLVER_EXCEPT).length();
    }
        
    private static List<StackTraceElement> getFramesArray() {
        // we know that's incorrect, but we want to test performance, not semantic correctness
        // it will have few (2-3) items more than should be
        return Arrays.asList(new Exception().getStackTrace());
    }
    
    public static void main(String[] args) throws RunnerException, IOException {
        BenchmarkRunner.run(Walker8Benchmark.class);
    }
    
}
