/* * Copyright 2014 Ben Manes. All Rights Reserved. * * Licensed 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 com.github.benmanes.caffeine.cache; import java.util.Random; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.Group; import org.openjdk.jmh.annotations.GroupThreads; import org.openjdk.jmh.annotations.Level; 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.openjdk.jmh.annotations.TearDown; import com.yahoo.ycsb.generator.NumberGenerator; import com.yahoo.ycsb.generator.ScrambledZipfianGenerator; /** * A benchmark that evaluates the read/write performance of a cache. The cache is pre-populated for * a 100% hit rate and a Zipf distribution of keys is used to mimic application usage patterns. * <p> * <pre>{@code * ./gradlew jmh -PincludePattern=GetPutBenchmark * }</pre> * * @author ben.manes@gmail.com (Ben Manes) */ @State(Scope.Group) public class GetPutBenchmark { private static final int SIZE = (2 << 14); private static final int MASK = SIZE - 1; private static final int ITEMS = SIZE / 3; @Param({ "LinkedHashMap_Lru", "Caffeine", "ConcurrentLinkedHashMap", "Guava", "ElasticSearch", "Jackrabbit", "Cache2k", "Ehcache2_Lru", "Ehcache3", "ExpiringMap_Fifo", "ExpiringMap_Lru", "TCache_Lfu", "TCache_Lru", "Rapidoid", }) CacheType cacheType; BasicCache<Integer, Boolean> cache; Integer[] ints; @State(Scope.Thread) public static class ThreadState { static final Random random = new Random(); int index = random.nextInt(); } @Setup public void setup() { ints = new Integer[SIZE]; cache = cacheType.create(2 * SIZE); // Enforce full initialization of internal structures for (int i = 0; i < 2 * SIZE; i++) { cache.put(i, Boolean.TRUE); } cache.clear(); cache.cleanUp(); // Populate with a realistic access distribution NumberGenerator generator = new ScrambledZipfianGenerator(ITEMS); for (int i = 0; i < SIZE; i++) { ints[i] = generator.nextValue().intValue(); cache.put(ints[i], Boolean.TRUE); } } @TearDown(Level.Iteration) public void tearDown() { cache.cleanUp(); } @Benchmark @Group("read_only") @GroupThreads(8) public Boolean readOnly(ThreadState threadState) { return cache.get(ints[threadState.index++ & MASK]); } @Benchmark @Group("write_only") @GroupThreads(8) public void writeOnly(ThreadState threadState) { cache.put(ints[threadState.index++ & MASK], Boolean.TRUE); } @Benchmark @Group("readwrite") @GroupThreads(6) public Boolean readwrite_get(ThreadState threadState) { return cache.get(ints[threadState.index++ & MASK]); } @Benchmark @Group("readwrite") @GroupThreads(2) public void readwrite_put(ThreadState threadState) { cache.put(ints[threadState.index++ & MASK], Boolean.TRUE); } }