使用JMH實際測試EhCache、GuavaCache和Caffeine之間的性能
測試代碼
EhCache
@BenchmarkMode({Mode.AverageTime}) @OutputTimeUnit(TimeUnit.MICROSECONDS) @Warmup(iterations=3, time = 5, timeUnit = TimeUnit.MILLISECONDS) @Measurement(iterations=5,time = 1,timeUnit = TimeUnit.SECONDS) @Threads(8) @Fork(3) @State(Scope.Thread) public class EhCacheTest { private static CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder() .build(true); private static Cache<String, String> cache = cacheManager.createCache("myCache", CacheConfigurationBuilder.newCacheConfigurationBuilder(String.class, String.class, ResourcePoolsBuilder.newResourcePoolsBuilder().heap(100, MemoryUnit.MB)) .withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofSeconds(1L))).build()); static { cache.put("test","test"); } @Benchmark public void test(){ cache.get("test"); } }
Guava Cache
@BenchmarkMode({Mode.AverageTime}) @OutputTimeUnit(TimeUnit.MICROSECONDS) @Warmup(iterations=3, time = 5, timeUnit = TimeUnit.MILLISECONDS) @Measurement(iterations=5,time = 1,timeUnit = TimeUnit.SECONDS) @Threads(8) @Fork(3) @State(Scope.Thread) public class GuavaTest { private static Cache<String,String> cache = CacheBuilder.newBuilder() .maximumSize(100) .expireAfterWrite(1,TimeUnit.SECONDS) .build(); static { cache.put("test","test"); } @Benchmark public void test(){ cache.getIfPresent("test"); } }
Caffeine
@BenchmarkMode({Mode.AverageTime}) @OutputTimeUnit(TimeUnit.MICROSECONDS) @Warmup(iterations=3, time = 5, timeUnit = TimeUnit.MILLISECONDS) @Measurement(iterations=5,time = 1,timeUnit = TimeUnit.SECONDS) @Threads(8) @Fork(3) @State(Scope.Thread) public class CaffeineTest { private static Cache<String,String> cache = Caffeine.newBuilder() .maximumSize(100) .expireAfterWrite(1,TimeUnit.SECONDS) .build(); static { cache.put("test","test"); } @Benchmark public void test(){ cache.getIfPresent("test"); } }
說明,實際測試代碼共有六種情況(其實還有別的情況,不加任何參數使Caffeine使用UnboundedLocalCache,性能應該還會改變,大家如果有興趣可以自己嘗試)
直接放結果
- 奇特的地方
- 在加過期時間的情況下三個緩存方案的性能均有所提升
- Guava不加過期時間的情況下高並發會OOM
- EhCache在加過期時間的情況下竟然比Guava的性能要好
- Caffeine讀比寫的性能要高很多
總結
- Guava使用jdk的Queue記錄緩存的寫讀情況,導致OOM;而Caffeine使用Disruptor的RingBuffer數據結構記錄
- Caffeine對寫的所有線程共用一個RingBuffer;而對讀的每個線程維護一個RingBuffer
- 使用Caffeine是你不會后悔的選擇
- 可參考Caffeine