/*
 * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
 * agreements. See the NOTICE file distributed with this work for additional information regarding
 * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance with the License. You may obtain a
 * copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */

package org.apache.geode.internal.lang;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;

import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Group;
import org.openjdk.jmh.annotations.GroupThreads;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;

import org.apache.geode.internal.lang.utils.JavaWorkarounds;


/**
 * Test spins up threads that constantly do computeIfAbsent
 * The tests will measure throughput
 * The benchmark tests computeIfAbsent in the presence of other threads contending for the same key
 *
 * Java 8:
 *
 * <pre>
 * Benchmark                                (impl)   Mode  Cnt           Score           Error  Units
 * ComputeIfAbsentBenchmark.group             noop  thrpt    5  1681567966.242 ± 100838863.427  ops/s
 * ComputeIfAbsentBenchmark.group           direct  thrpt    5    10085547.968 ±    242865.843  ops/s
 * ComputeIfAbsentBenchmark.group       workaround  thrpt    5   901693027.158 ±  24905576.450  ops/s
 * </pre>
 *
 * Java 11:
 *
 * <pre>
 * Benchmark                                (impl)   Mode  Cnt           Score           Error  Units
 * ComputeIfAbsentBenchmark.group             noop  thrpt    5  1550792591.631 ± 112841732.003  ops/s
 * ComputeIfAbsentBenchmark.group           direct  thrpt    5   545381612.922 ±  82857066.730  ops/s
 * ComputeIfAbsentBenchmark.group       workaround  thrpt    5   954400194.346 ±  63544640.622  ops/s
 * </pre>
 */
@State(Scope.Benchmark)
@Fork(1)
public class ComputeIfAbsentBenchmark {

  public Map<Integer, Integer> map = new ConcurrentHashMap<>();

  public enum Impl {
    noop, direct, workaround
  }

  @Param()
  public Impl impl;
  public Function<Integer, Integer> accessor;

  final Function<Integer, Integer> noopAccessor = t -> t;
  final Function<Integer, Integer> directAccessor = t -> map.computeIfAbsent(t, k -> k);
  final Function<Integer, Integer> workaroundAccessor =
      t -> JavaWorkarounds.computeIfAbsent(map, t, k -> k);

  @Setup
  public void setup() {
    switch (impl) {
      case noop:
        accessor = noopAccessor;
        break;
      case direct:
        accessor = directAccessor;
        break;
      case workaround:
        accessor = workaroundAccessor;
        break;
      default:
        throw new IllegalStateException();
    }
  }

  @Benchmark
  @Group()
  @GroupThreads(10)
  public Integer load() {
    return accessor.apply(1);
  }

  @Benchmark
  @Group()
  @GroupThreads(1)
  public Integer work() {
    return accessor.apply(1);
  }

}
