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