在 Guava Cache 对象上启用统计信息的性能损失是多少?
What is the performance penalty of enabling stats on Guava Cache objects?
显然,正确答案是 'benchmark it and find out',但本着互联网的精神,我希望有人能为我完成这项工作。
我非常喜欢 Guava 的网络服务缓存库。然而,他们的文档在这一点上相当模糊。
recordStats
public CacheBuilder<K,V> recordStats()
Enable the accumulation of CacheStats
during the operation of the cache. Without this Cache.stats()
will return zero for all statistics. Note that recording stats requires bookkeeping to be performed with each operation, and thus imposes a performance penalty on cache operation.
Since:
12.0 (previously, stats collection was automatic)
来自 CacheBuilder.recordStats()
.
的 JavaDocs
我很好奇性能损失的严重程度是否被任何人记录、基准测试或模拟。我认为它应该很小,每次操作的数量级为纳秒。缓存操作本身已经同步 - 读取不会锁定或阻塞,但写入会获取锁定 - 因此不需要额外的锁定或并发来修改统计信息。这应该将其限制为每次缓存访问一些额外的增量操作。
它的另一面可能是调用 Cache.stats()
时的一些惩罚。我计划通过 Codahale MetricsRegistry 将统计数据公开到 Graphite 服务器上进行持久记录。最终效果是会定期检索统计信息,因此如果检索时出现任何阻塞行为,那可能会很糟糕。
我们来看看 source code:
当我们调用 CacheBuilder.recordStats()
时会发生什么?
CacheBuilder
defines a no-op StatsCounter
implementation NULL_STATS_COUNTER
and this is what is used by default. If you call .recordStats()
this is replaced with SimpleStatsCounter
which has six LongAddable
fields (which is usually a LongAdder
but falls back to an AtomicLong
如果它不能对它跟踪的每个统计信息使用 LongAdder
)。
那我们构造一个Cache
会怎么样呢?
对于相同 StatsCounter
类型的标准 LocalCache
(which is what you get from CacheBuilder.build()
or CacheBuilder.build(CacheLoader)
), it constructs an instance of the desired StatsCounter
during construction. Each Segment
of the Cache
similarly gets its own instance。其他 Cache
实现可以根据需要选择使用 SimpleStatsCounter
,或者提供自己的行为(例如无操作实现)。
而当我们使用 Cache
?
每次对 LocalCache
的调用都会影响其中一项统计信息,并调用相关的 StatsCounter.record*()
方法,这反过来会导致支持 LongAddable
上的原子增量或添加。 LongAdder
被记录为比 AtomicLong
快得多,所以就像你说的那样,这应该很难被注意到。尽管在无操作 StatsRecorder
的情况下,JIT 可以完全优化掉 record*()
调用,随着时间的推移,这 可能 会很明显。但决定不在此基础上跟踪统计数据肯定是 premature optimization。
最后我们什么时候得到统计数据?
当您在新的 StatsCounter
中调用 Cache.stats()
the StatsCounter
s for the Cache
and all its Segments
are aggregated together 并将结果返回给您时。这意味着将有最少的阻塞;每个字段只需要读取一次,并且没有外部同步或锁定。这确实意味着技术上存在竞争条件(可以在聚合中途访问段),但实际上这无关紧要。
总而言之?
在您有兴趣监视的任何 Cache
上使用 CacheBuilder.recordStats()
并尽可能频繁地调用 Cache.stats()
是有益的,您应该感到自在。内存开销大致恒定,速度开销可以忽略不计(并且比您可能实施的任何类似监控更快),Cache.stats()
.
的争用开销也是如此
显然,专用线程除了在循环中调用 Cache.stats()
什么都不做会引起一些争用,但那是愚蠢的。任何类型的定期访问都不会引起注意。
显然,正确答案是 'benchmark it and find out',但本着互联网的精神,我希望有人能为我完成这项工作。
我非常喜欢 Guava 的网络服务缓存库。然而,他们的文档在这一点上相当模糊。
recordStats
public CacheBuilder<K,V> recordStats()
Enable the accumulation ofCacheStats
during the operation of the cache. Without thisCache.stats()
will return zero for all statistics. Note that recording stats requires bookkeeping to be performed with each operation, and thus imposes a performance penalty on cache operation.Since:
12.0 (previously, stats collection was automatic)
来自 CacheBuilder.recordStats()
.
我很好奇性能损失的严重程度是否被任何人记录、基准测试或模拟。我认为它应该很小,每次操作的数量级为纳秒。缓存操作本身已经同步 - 读取不会锁定或阻塞,但写入会获取锁定 - 因此不需要额外的锁定或并发来修改统计信息。这应该将其限制为每次缓存访问一些额外的增量操作。
它的另一面可能是调用 Cache.stats()
时的一些惩罚。我计划通过 Codahale MetricsRegistry 将统计数据公开到 Graphite 服务器上进行持久记录。最终效果是会定期检索统计信息,因此如果检索时出现任何阻塞行为,那可能会很糟糕。
我们来看看 source code:
当我们调用 CacheBuilder.recordStats()
时会发生什么?
CacheBuilder
defines a no-op StatsCounter
implementation NULL_STATS_COUNTER
and this is what is used by default. If you call .recordStats()
this is replaced with SimpleStatsCounter
which has six LongAddable
fields (which is usually a LongAdder
but falls back to an AtomicLong
如果它不能对它跟踪的每个统计信息使用 LongAdder
)。
那我们构造一个Cache
会怎么样呢?
对于相同 StatsCounter
类型的标准 LocalCache
(which is what you get from CacheBuilder.build()
or CacheBuilder.build(CacheLoader)
), it constructs an instance of the desired StatsCounter
during construction. Each Segment
of the Cache
similarly gets its own instance。其他 Cache
实现可以根据需要选择使用 SimpleStatsCounter
,或者提供自己的行为(例如无操作实现)。
而当我们使用 Cache
?
每次对 LocalCache
的调用都会影响其中一项统计信息,并调用相关的 StatsCounter.record*()
方法,这反过来会导致支持 LongAddable
上的原子增量或添加。 LongAdder
被记录为比 AtomicLong
快得多,所以就像你说的那样,这应该很难被注意到。尽管在无操作 StatsRecorder
的情况下,JIT 可以完全优化掉 record*()
调用,随着时间的推移,这 可能 会很明显。但决定不在此基础上跟踪统计数据肯定是 premature optimization。
最后我们什么时候得到统计数据?
当您在新的 StatsCounter
中调用 Cache.stats()
the StatsCounter
s for the Cache
and all its Segments
are aggregated together 并将结果返回给您时。这意味着将有最少的阻塞;每个字段只需要读取一次,并且没有外部同步或锁定。这确实意味着技术上存在竞争条件(可以在聚合中途访问段),但实际上这无关紧要。
总而言之?
在您有兴趣监视的任何 Cache
上使用 CacheBuilder.recordStats()
并尽可能频繁地调用 Cache.stats()
是有益的,您应该感到自在。内存开销大致恒定,速度开销可以忽略不计(并且比您可能实施的任何类似监控更快),Cache.stats()
.
显然,专用线程除了在循环中调用 Cache.stats()
什么都不做会引起一些争用,但那是愚蠢的。任何类型的定期访问都不会引起注意。