package com.test;


import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;

@Fork(value = 1 /*, jvmArgsAppend = {"-XX:+UnlockDiagnosticVMOptions", "-Xlog:class+load=info", "-XX:+LogCompilation", "-XX:+PrintAssembly", "-XX:+DebugNonSafepoints"}*/)
@Warmup(iterations = 2, time = 6)
@Measurement(iterations = 2, time = 10)
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.SECONDS)
public class Bench {
    @State(Scope.Benchmark)
    public static class BenchState {
        public static final VarHandle AGE_DIRECT_VAR_HANDLE;
        public static final IntVarHandle AGE_INDIRECT_VAR_HANDLE;
        static {
            try {
                AGE_DIRECT_VAR_HANDLE = MethodHandles.privateLookupIn(Person.class, MethodHandles.lookup())
                        .findVarHandle(Person.class, "age", int.class);
                AGE_INDIRECT_VAR_HANDLE = new IntVarHandle(AGE_DIRECT_VAR_HANDLE);
            } catch (Throwable e) {
                throw new RuntimeException(e);
            }
        }
        Person[] data;
        @Setup
        public void setup() {
            ThreadLocalRandom random = ThreadLocalRandom.current();
            data = Stream.generate(Person::new)
                    .limit(4096)
                    .peek(p -> p.age = random.nextInt())
                    .toArray(Person[]::new);
        }
    }
    @Benchmark
    public long benchmark_direct_var_handle(BenchState state) {
        long acc = 0;
        for (int i = 0; i < state.data.length; i++) {
            acc += (int) BenchState.AGE_DIRECT_VAR_HANDLE.get(state.data[i]);
        }
        return acc;
    }
    @Benchmark
    public long benchmark_indirect_var_handle(BenchState state) {
        long acc = 0;
        for (int i = 0; i < state.data.length; i++) {
            acc += BenchState.AGE_INDIRECT_VAR_HANDLE.getInt(state.data[i]);
        }
        return acc;
    }
    public final static class Person {
        public int age;
    }
    public abstract static class TypedVarHandle {
    }
    public static class IntVarHandle extends TypedVarHandle {
        private final VarHandle varHandle;
        public IntVarHandle(VarHandle varHandle) {
            this.varHandle = varHandle;
        }
        public int getInt(Person o) {
            return (int) varHandle.get(o);
        }
    }
}