问题描述
显然,正确的答案是“基准测试并找出答案”,但本着互联网的精神,我希望有人能为我完成这项工作。
我非常喜欢Guava用于Web服务的缓存库。 然而,他们的文档在这一点上相当模糊。
recordStats
public CacheBuilder<K,V> recordStats()
在缓存操作期间启用的累积。 如果没有这个,Cache.stats()
将为所有统计信息返回零。 请注意,记录统计信息需要对每个操作执行簿记,因此会对高速缓存操作造成性能损失。以来:
12.0(之前,统计数据收集是自动的)
从JavaDocs for 。
我很好奇性能惩罚的严重程度是否由任何人记录,基准或停球。 我认为它应该是非常小的,每次操作的纳秒数量级。 缓存操作本身已经同步 - 读取不会锁定或阻塞,但写入会获取锁定 - 因此不需要额外的锁定或并发来修改统计信息。 这应该限制为每个缓存访问的一些额外的增量操作。
调用Cache.stats()
时,它的另一面可能是一些惩罚。
我计划通过Codahale MetricsRegistry和Graphite服务器将统计信息暴露给持久记录。
净效应是定期检索统计数据,因此如果检索有任何阻塞行为,那可能会很糟糕。
1楼
让我们来看看 :
当我们调用CacheBuilder.recordStats()
时会发生什么?
定义了一个无操作的实现 ,这是默认使用的。
如果你调用.recordStats()
,它将替换为 ,它有六个字段(通常是但如果它不能使用LongAdder
回退到 ),用于它跟踪的每个统计数据。
那么当我们构造一个Cache
时会发生什么?
对于标准 (这是从CacheBuilder.build()
或CacheBuilder.build(CacheLoader)
),它所需StatsCounter
的实例。
Cache
每个类似地获得的相同StatsCounter
类型 。
其他Cache
实现可以选择使用SimpleStatsCounter
如果他们愿意,或提供他们自己的行为(例如,无操作实现)。
当我们使用Cache
?
对LocalCache
每次调用都会影响其中一个统计信息,调用相关的StatsCounter.record*()
方法,这反过来会导致支持LongAddable
的原子增量或添加。
LongAdder
比AtomicLong
快得多,所以就像你说的那样,这应该是难以察觉的。
虽然在无操作的情况下StatsRecorder
的JIT可以优化掉的record*()
全部调用,这也许可以随着时间的推移明显。
但决定不在此基础上追踪统计数据肯定会 。
最后,当我们得到统计数据?
当您调用 , Cache
的StatsCounter
及其所有Segments
将在新的StatsCounter
中 ,结果将返回给您。
这意味着将有最小的阻止;
每个字段只需要读取一次,并且没有外部同步或锁定。
这确实意味着技术上存在竞争条件(可以在聚合的中途访问一个分段),但实际上这是无关紧要的。
总结一下?
您应该在您感兴趣监视的任何Cache
上使用CacheBuilder.recordStats()
,并尽可能频繁地调用Cache.stats()
。
内存开销大致保持不变,速度开销可以忽略不计(并且比您可能实现的任何类似监视更快), Cache.stats()
的争用开销也是Cache.stats()
。
显然,一个专用线程除了在循环中调用Cache.stats()
什么都不做会引起一些争用,但这很愚蠢。
任何类型的定期访问都将被忽视。