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

import com.google.caliper.SimpleBenchmark;
import java.util.Arrays;
import java.util.List;
import net.ranides.assira.junit.BenchmarkRunner;

/**
 * Benchmark for different methods of obtaining list of caller-classess.
 * 
 * SecurityManager creates list ~10 times faster than stacktrace.
 * SecurityManager gets caller  ~15 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 ~2 times faster that stacktrace.
 * SunReflection gets caller  ~100 times faster than stacktrace.
 * Best if you need first class.
 * It's nonportable, but it will work even if you disable SecurityManager.
 * 
 * 
 * Notice: JDK8 Stream API is very convenient but it has too large overhead. 
 * As effect we removed old "elegant" code. It's unacceptable to sacrifice 25%
 * of execution time because we want to use functional interfaces.
 * Streams:
 *      SecurityManager     =  1.1 us
 *      SunReflection       =  4.2 us
 *      Exception           = 10.7 us
 *      Thread              = 11.3 us
 * Arrays:
 *      SecurityManager     =  0.8 us   72 %
 *      SunReflection       =  3.6 us   85 %
 *      Exception           = 10.5 us   98 %
 *      Thread              = 11.1 us   98 %
 *
 * 
 * Most recent results for JDK 1.8.0 (Lenovo G510 ranides-laptop)
 *
 *      CallerSunReflect         =   144 ? 2
 *      CallerSecurityManager    =   822 ? 6
 *      CallerException          =  1946 ? 3
 *      CallerThread             = 13668 ? 71
 * 
 *      ListSecurityManager      =   842 ? 61
 *      ListSunReflect           =  3794 ? 24
 *      ListException            = 12006 ? 44
 *      ListThread               = 12681 ? 147
 *
 *      FrameNative              =  1769 ? 17
 *      FrameDefault             =  9903 ? 16
 *      FrameListNative          = 10265 ? 45 
 *      FrameListDefault         = 10043 ? 20
 * 
 * @author Ranides Atterwim <ranides@gmail.com>
 * 
 * @todo (assira #5) benchmark: switch to JMH framework
 */
public class StackInspectorBenchmark extends SimpleBenchmark {
    
    public int timeFrameNative(int reps) {
        int ret = 0;
        for(int i=0; i<reps; i++) {
            ret += TraceUtils.getFrames().get(1).getLineNumber();
        }
        return ret;
    }
    
    
    public int timeFrameDefault(int reps) {
        int ret = 0;
        for(int i=0; i<reps; i++) {
            ret += getFramesArray().get(1).getLineNumber();
        }
        return ret;
    }
    
     public int timeFrameListNative(int reps) {
        int ret = 0;
        for(int i=0; i<reps; i++) {
            for(StackTraceElement e : TraceUtils.getFrames()) {
                ret += e.getLineNumber();
            }
        }
        return ret;
    }
    
    public int timeFrameListDefault(int reps) {
        int ret = 0;
        for(int i=0; i<reps; i++) {
            for(StackTraceElement e : getFramesArray()) {
                ret += e.getLineNumber();
            }
        }
        return ret;
    }
    
    public int timeListSecurityManager(int reps) {
        int ret = 0;
        for(int i=0; i<reps; i++) {
            ret += TraceUtils.test_getNames(TraceUtils.RESOLVER_SM).size();
        }
        return ret;
    }
    
    public int timeListThread(int reps) {
        int ret = 0;
        for(int i=0; i<reps; i++) {
            ret += TraceUtils.test_getNames(TraceUtils.RESOLVER_TS).size();
        }
        return ret;
    }
    
    public int timeListSunReflect(int reps) {
        int ret = 0;
        for(int i=0; i<reps; i++) {
            ret += TraceUtils.test_getNames(TraceUtils.RESOLVER_SR).size();
        }
        return ret;
    }
    
    public int timeListException(int reps) {
        int ret = 0;
        for(int i=0; i<reps; i++) {
            ret += TraceUtils.test_getNames(TraceUtils.RESOLVER_ES).size();
        }
        return ret;
    }
    
    public int timeCallerSecurityManager(int reps) {
        int ret = 0;
        for(int i=0; i<reps; i++) {
            ret += TraceUtils.test_getCalleeName(TraceUtils.RESOLVER_SM).length();
        }
        return ret;
    }
    
    public int timeCallerThread(int reps) {
        int ret = 0;
        for(int i=0; i<reps; i++) {
            ret += TraceUtils.test_getCalleeName(TraceUtils.RESOLVER_TS).length();
        }
        return ret;
    }
    
    public int timeCallerSunReflect(int reps) {
        int ret = 0;
        for(int i=0; i<reps; i++) {
            ret += TraceUtils.test_getCalleeName(TraceUtils.RESOLVER_SR).length();
        }
        return ret;
    }
    
    public int timeCallerException(int reps) {
        int ret = 0;
        for(int i=0; i<reps; i++) {
            ret += TraceUtils.test_getCalleeName(TraceUtils.RESOLVER_ES).length();
        }
        return ret;
    }
    
    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) {
        BenchmarkRunner.run(StackInspectorBenchmark.class);
    }
    
}
