According to these micro benchmarks it turns out that Caffeine is a way faster than Guava cache in both read and write operations.
What is the secret of Caffeine implementation? How it differs from the Guava Cache?
Am I right that in case of timed expiration Caffeine use a scheduled executor to perform appropriate maintenance operations in background?
Caffeine cache is a high-performance cache library for Java.
Values are automatically loaded by the cache asynchronously, and are stored in the cache until either evicted or manually invalidated. Implementations of this interface are expected to be thread-safe, and can be safely accessed by multiple concurrent threads.
Cache. Caffeine provides an in-memory cache using a Google Guava inspired API. The improvements draw on our experience designing Guava's cache and ConcurrentLinkedHashMap.
The Guava Cache is an incremental cache, in the sense that when you request an object from the cache, it checks to see if it already has the corresponding value for the supplied key. If it does, it simply returns it (assuming it hasn't expired).
The main difference is because Caffeine uses ring buffers to record & replay events, whereas Guava uses ConcurrentLinkedQueue
. The intent was always to migrate Guava over and it made sense to start simpler, but unfortunately there was never interest in accepting those changes. The ring buffer approach avoids allocation, is bounded (lossy), and cheaper to operate against.
The remaining costs are due to a design mismatch. The original author of MapMaker
was enthusiastic about soft references as the solution to caching problems by deferring it to the GC. Unfortunately while that can seem fast in microbenchmarks, it has horrible performance in practice due to causing stop-the-world GC thrashing. The size-based solution had to be adapted into this work and that is not ideal. Caffeine optimizes for size-based and also gains an improved hash table, whereas Guava handles reference caching more elegantly.
Caffeine doesn't create its own threads for maintenance or expiration. It does defer the cost to the commonPool
, which slightly improves user-facing latencies but not throughput. A future version might leverage CompletableFuture.delayedExecutor
to schedule the next expiration event without directly creating threads (for users who have business logic depending on prompt removal notifications).
ConcurrentLinkedHashMap
and MapMaker
were written at the same time and CLHM has similar performance to Caffeine. I believe the difference is due to what scenarios the designers favored and optimized for, which impacted how other features would be implemented. There is low hanging fruit to allow Guava to have similar performance profile, but there isn't an internal champion to drive that (and even less so with Caffeine as a favored alternative).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With