咖啡因与番石榴缓存

Caffeine versus Guava cache

根据这些micro benchmarks it turns out that Caffeine is a way faster than Guava cache在读写操作中。

Caffeine 实现的秘诀是什么?它与 Guava 缓存有何不同?

在定时到期的情况下,Caffeine 使用预定的执行程序在后台执行适当的维护操作,我说得对吗?

主要区别在于 Caffeine 使用环形缓冲区记录和重放事件,而 Guava 使用 ConcurrentLinkedQueue。意图始终是将 Guava 迁移过来,从更简单的开始是有意义的,但不幸的是,人们从来没有兴趣接受这些变化。环形缓冲区方法避免了分配,有界(有损),并且操作成本更低。

剩余成本是由于设计不匹配造成的。 MapMaker 的原作者热衷于通过将软引用推迟到 GC 来解决缓存问题。不幸的是,虽然这在微基准测试中看起来很快,但由于导致 stop-the-world GC 抖动,它在实践中的性能很糟糕。基于大小的解决方案必须适应这项工作,这并不理想。 Caffeine 针对基于大小进行了优化,并且还获得了改进的哈希 table,而 Guava 更优雅地处理了引用缓存。

Caffeine 不会为维护或过期创建自己的线程。它确实将成本推迟到 commonPool,这会稍微改善面向用户的延迟,但不会改善吞吐量。未来的版本可能会利用 CompletableFuture.delayedExecutor 来安排下一个过期事件,而无需直接创建线程(对于具有依赖于提示删除通知的业务逻辑的用户)。

ConcurrentLinkedHashMapMapMaker是同时写的,CLHM的性能和Caffeine差不多。我认为差异是由于设计人员偏爱和优化的场景,这影响了其他功能的实现方式。番石榴具有类似的性能特征是很容易实现的,但没有内部冠军来推动它(更不用说咖啡因作为受欢迎的替代品了)。