package de.jpaw.bonaparte.benchmarks.map; import java.util.HashMap; import java.util.Map; import java.util.Random; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; import org.openjdk.jmh.annotations.Mode; import org.openjdk.jmh.annotations.OperationsPerInvocation; 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.infra.Blackhole; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; // Benchmarks to investigate how much performance impact setting an access or write timeout has to guava cache. //java -jar target/bonaparte-benchmarks.jar -i 3 -f 3 -wf 1 -wi 3 //Benchmark Mode Samples Mean Mean error Units //d.j.b.b.m.GuavaCache.mapGet thrpt 9 10160.454 208.834 ops/ms //d.j.b.b.m.GuavaCache.guavaGetNoTO thrpt 9 2691.573 240.131 ops/ms //d.j.b.b.m.GuavaCache.guavaGetRdTO thrpt 9 1182.600 30.350 ops/ms //d.j.b.b.m.GuavaCache.guavaGetWrTO thrpt 9 1630.863 39.137 ops/ms //d.j.b.b.m.GuavaCache.mapPut thrpt 9 23428.648 949.636 ops/ms //d.j.b.b.m.GuavaCache.guavaPutNoTO thrpt 9 5963.303 206.986 ops/ms //d.j.b.b.m.GuavaCache.guavaPutRdTO thrpt 9 4005.642 227.445 ops/ms //d.j.b.b.m.GuavaCache.guavaPutWrTO thrpt 9 3969.242 229.006 ops/ms // Result: // for GET, Map is about 4-8 times faster than Cache, depending on if timeouts are set. // for PUT, Map is about 4-6 times faster, depending on if timeouts are set. // timings ns: // run results: //Benchmark Mode Cnt Score Error Units //GuavaCache.chmGet avgt 9 105.686 ± 11.172 ns/op //GuavaCache.chmPut avgt 9 43.938 ± 2.545 ns/op //GuavaCache.guavaGetNoTO avgt 9 373.119 ± 39.137 ns/op //GuavaCache.guavaGetRdTO avgt 9 859.240 ± 21.157 ns/op //GuavaCache.guavaGetWrTO avgt 9 573.457 ± 10.610 ns/op //GuavaCache.guavaPutNoTO avgt 9 120.493 ± 12.440 ns/op //GuavaCache.guavaPutRdTO avgt 9 183.040 ± 10.048 ns/op //GuavaCache.guavaPutWrTO avgt 9 176.098 ± 11.642 ns/op //GuavaCache.hmGet avgt 9 93.889 ± 10.426 ns/op //GuavaCache.hmPut avgt 9 22.735 ± 8.615 ns/op // net result: put = 1:1, get = (measured - put) / 4 // // hm = 18 / 23 // chm = 15 / 44 // guava no TO = 62 / 120 // guava rd TO = 170 / 180 // guava wr TO = 100 / 180 // => expiry timeouts add a significant overhead. Guava is a lot slower than Java hashMap anyway // => for read operations, chm is not slower than hm (within measurement precision) @State(value = Scope.Thread) @OutputTimeUnit(TimeUnit.NANOSECONDS) @BenchmarkMode(Mode.AverageTime) @OperationsPerInvocation(GuavaCache.OPERATIONS_PER_INVOCATION) public class GuavaCache { static public final int OPERATIONS_PER_INVOCATION = 100000; public final Integer [] numbers = new Integer[OPERATIONS_PER_INVOCATION]; public final Integer [] id = new Integer[OPERATIONS_PER_INVOCATION]; public Cache<Integer,Integer> cacheNoTO; public Cache<Integer,Integer> cacheWrTO; public Cache<Integer,Integer> cacheRdTO; final Map<Integer,Integer> chm = new ConcurrentHashMap<Integer,Integer>(2 * OPERATIONS_PER_INVOCATION); final Map<Integer,Integer> hm = new HashMap<Integer,Integer>(2 * OPERATIONS_PER_INVOCATION); @Setup public void init() { Random rnd = new Random(2846284628L); for (int i = 0; i < OPERATIONS_PER_INVOCATION; ++i) { numbers[i] = Integer.valueOf(rnd.nextInt()); id[i] = Integer.valueOf(i); } cacheNoTO = CacheBuilder .newBuilder() .build(); cacheWrTO = CacheBuilder .newBuilder() .expireAfterWrite(60, TimeUnit.SECONDS).build(); cacheRdTO = CacheBuilder .newBuilder() .expireAfterAccess(60, TimeUnit.SECONDS).build(); } private void fillCache(Cache<Integer,Integer> cache) { for (int i = 0; i < OPERATIONS_PER_INVOCATION; ++i) cache.put(numbers[i], id[i]); } private void readCache(Cache<Integer,Integer> cache, Blackhole bh) { for (int i = 0; i < OPERATIONS_PER_INVOCATION; ++i) bh.consume(cache.getIfPresent(numbers[i])); } private void fillMap(Map<Integer,Integer> cache) { for (int i = 0; i < OPERATIONS_PER_INVOCATION; ++i) cache.put(numbers[i], id[i]); } private void readMap(Map<Integer,Integer> cache, Blackhole bh) { for (int i = 0; i < OPERATIONS_PER_INVOCATION; ++i) bh.consume(cache.get(numbers[i])); } @Benchmark public void guavaPutNoTO(Blackhole bh) { fillCache(cacheNoTO); } @Benchmark public void guavaPutWrTO(Blackhole bh) { fillCache(cacheWrTO); } @Benchmark public void guavaPutRdTO(Blackhole bh) { fillCache(cacheRdTO); } @Benchmark public void chmPut(Blackhole bh) { fillMap(chm); } @Benchmark public void hmPut(Blackhole bh) { fillMap(hm); } @Benchmark public void guavaGetNoTO(Blackhole bh) { fillCache(cacheNoTO); readCache(cacheNoTO, bh); readCache(cacheNoTO, bh); readCache(cacheNoTO, bh); readCache(cacheNoTO, bh); } @Benchmark public void guavaGetWrTO(Blackhole bh) { fillCache(cacheWrTO); readCache(cacheWrTO, bh); readCache(cacheWrTO, bh); readCache(cacheWrTO, bh); readCache(cacheWrTO, bh); } @Benchmark public void guavaGetRdTO(Blackhole bh) { fillCache(cacheRdTO); readCache(cacheRdTO, bh); readCache(cacheRdTO, bh); readCache(cacheRdTO, bh); readCache(cacheRdTO, bh); } @Benchmark public void chmGet(Blackhole bh) { fillMap(chm); readMap(chm, bh); readMap(chm, bh); readMap(chm, bh); readMap(chm, bh); } @Benchmark public void hmGet(Blackhole bh) { fillMap(hm); readMap(hm, bh); readMap(hm, bh); readMap(hm, bh); readMap(hm, bh); } }