001/*
002 * Copyright (C) 2011 The Guava Authors
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package com.google.common.cache;
018
019import static com.google.common.base.Preconditions.checkArgument;
020
021import com.google.common.annotations.Beta;
022import com.google.common.annotations.GwtCompatible;
023import com.google.common.base.Objects;
024
025import javax.annotation.Nullable;
026
027/**
028 * Statistics about the performance of a {@link Cache}. Instances of this class are immutable.
029 *
030 * <p>Cache statistics are incremented according to the following rules:
031 *
032 * <ul>
033 * <li>When a cache lookup encounters an existing cache entry {@code hitCount} is incremented.
034 * <li>When a cache lookup first encounters a missing cache entry, a new entry is loaded.
035 * <ul>
036 * <li>After successfully loading an entry {@code missCount} and {@code loadSuccessCount} are
037 *     incremented, and the total loading time, in nanoseconds, is added to
038 *     {@code totalLoadTime}.
039 * <li>When an exception is thrown while loading an entry, {@code missCount} and {@code
040 *     loadExceptionCount} are incremented, and the total loading time, in nanoseconds, is
041 *     added to {@code totalLoadTime}.
042 * <li>Cache lookups that encounter a missing cache entry that is still loading will wait
043 *     for loading to complete (whether successful or not) and then increment {@code missCount}.
044 * </ul>
045 * <li>When an entry is evicted from the cache, {@code evictionCount} is incremented.
046 * <li>No stats are modified when a cache entry is invalidated or manually removed.
047 * <li>No stats are modified by operations invoked on the {@linkplain Cache#asMap asMap} view of
048 *     the cache.
049 * </ul>
050 *
051 * @author Charles Fry
052 * @since 10.0
053 */
054@Beta
055@GwtCompatible
056public final class CacheStats {
057  private final long hitCount;
058  private final long missCount;
059  private final long loadSuccessCount;
060  private final long loadExceptionCount;
061  private final long totalLoadTime;
062  private final long evictionCount;
063
064  /**
065   * Constructs a new {@code CacheStats} instance.
066   *
067   * <p>Five parameters of the same type in a row is a bad thing, but this class is not constructed
068   * by end users and is too fine-grained for a builder.
069   */
070  public CacheStats(long hitCount, long missCount, long loadSuccessCount,
071      long loadExceptionCount, long totalLoadTime, long evictionCount) {
072    checkArgument(hitCount >= 0);
073    checkArgument(missCount >= 0);
074    checkArgument(loadSuccessCount >= 0);
075    checkArgument(loadExceptionCount >= 0);
076    checkArgument(totalLoadTime >= 0);
077    checkArgument(evictionCount >= 0);
078
079    this.hitCount = hitCount;
080    this.missCount = missCount;
081    this.loadSuccessCount = loadSuccessCount;
082    this.loadExceptionCount = loadExceptionCount;
083    this.totalLoadTime = totalLoadTime;
084    this.evictionCount = evictionCount;
085  }
086
087  /**
088   * Returns the number of times {@link Cache} lookup methods have returned either a cached or
089   * uncached value. This is defined as {@code hitCount + missCount}.
090   */
091  public long requestCount() {
092    return hitCount + missCount;
093  }
094
095  /**
096   * Returns the number of times {@link Cache} lookup methods have returned a cached value.
097   */
098  public long hitCount() {
099    return hitCount;
100  }
101
102  /**
103   * Returns the ratio of cache requests which were hits. This is defined as
104   * {@code hitCount / requestCount}, or {@code 1.0} when {@code requestCount == 0}.
105   * Note that {@code hitRate + missRate =~ 1.0}.
106   */
107  public double hitRate() {
108    long requestCount = requestCount();
109    return (requestCount == 0) ? 1.0 : (double) hitCount / requestCount;
110  }
111
112  /**
113   * Returns the number of times {@link Cache} lookup methods have returned an uncached (newly
114   * loaded) value, or null. Multiple concurrent calls to {@link Cache} lookup methods on an absent
115   * value can result in multiple misses, all returning the results of a single cache load
116   * operation.
117   */
118  public long missCount() {
119    return missCount;
120  }
121
122  /**
123   * Returns the ratio of cache requests which were misses. This is defined as
124   * {@code missCount / requestCount}, or {@code 0.0} when {@code requestCount == 0}.
125   * Note that {@code hitRate + missRate =~ 1.0}. Cache misses include all requests which
126   * weren't cache hits, including requests which resulted in either successful or failed loading
127   * attempts, and requests which waited for other threads to finish loading. It is thus the case
128   * that {@code missCount &gt;= loadSuccessCount + loadExceptionCount}. Multiple
129   * concurrent misses for the same key will result in a single load operation.
130   */
131  public double missRate() {
132    long requestCount = requestCount();
133    return (requestCount == 0) ? 0.0 : (double) missCount / requestCount;
134  }
135
136  /**
137   * Returns the total number of times that {@link Cache} lookup methods attempted to load new
138   * values. This includes both successful load operations, as well as those that threw
139   * exceptions. This is defined as {@code loadSuccessCount + loadExceptionCount}.
140   */
141  public long loadCount() {
142    return loadSuccessCount + loadExceptionCount;
143  }
144
145  /**
146   * Returns the number of times {@link Cache} lookup methods have successfully loaded a new value.
147   * This is always incremented in conjunction with {@link #missCount}, though {@code missCount}
148   * is also incremented when an exception is encountered during cache loading (see
149   * {@link #loadExceptionCount}). Multiple concurrent misses for the same key will result in a
150   * single load operation.
151   */
152  public long loadSuccessCount() {
153    return loadSuccessCount;
154  }
155
156  /**
157   * Returns the number of times {@link Cache} lookup methods threw an exception while loading a
158   * new value. This is always incremented in conjunction with {@code missCount}, though
159   * {@code missCount} is also incremented when cache loading completes successfully (see
160   * {@link #loadSuccessCount}). Multiple concurrent misses for the same key will result in a
161   * single load operation.
162   */
163  public long loadExceptionCount() {
164    return loadExceptionCount;
165  }
166
167  /**
168   * Returns the ratio of cache loading attempts which threw exceptions. This is defined as
169   * {@code loadExceptionCount / (loadSuccessCount + loadExceptionCount)}, or
170   * {@code 0.0} when {@code loadSuccessCount + loadExceptionCount == 0}.
171   */
172  public double loadExceptionRate() {
173    long totalLoadCount = loadSuccessCount + loadExceptionCount;
174    return (totalLoadCount == 0)
175        ? 0.0
176        : (double) loadExceptionCount / totalLoadCount;
177  }
178
179  /**
180   * Returns the total number of nanoseconds the cache has spent loading new values. This can be
181   * used to calculate the miss penalty. This value is increased every time
182   * {@code loadSuccessCount} or {@code loadExceptionCount} is incremented.
183   */
184  public long totalLoadTime() {
185    return totalLoadTime;
186  }
187
188  /**
189   * Returns the average time spent loading new values. This is defined as
190   * {@code totalLoadTime / (loadSuccessCount + loadExceptionCount)}.
191   */
192  public double averageLoadPenalty() {
193    long totalLoadCount = loadSuccessCount + loadExceptionCount;
194    return (totalLoadCount == 0)
195        ? 0.0
196        : (double) totalLoadTime / totalLoadCount;
197  }
198
199  /**
200   * Returns the number of times an entry has been evicted. This count does not include manual
201   * {@linkplain Cache#invalidate invalidations}.
202   */
203  public long evictionCount() {
204    return evictionCount;
205  }
206
207  /**
208   * Returns a new {@code CacheStats} representing the difference between this {@code CacheStats}
209   * and {@code other}. Negative values, which aren't supported by {@code CacheStats} will be
210   * rounded up to zero.
211   */
212  public CacheStats minus(CacheStats other) {
213    return new CacheStats(
214        Math.max(0, hitCount - other.hitCount),
215        Math.max(0, missCount - other.missCount),
216        Math.max(0, loadSuccessCount - other.loadSuccessCount),
217        Math.max(0, loadExceptionCount - other.loadExceptionCount),
218        Math.max(0, totalLoadTime - other.totalLoadTime),
219        Math.max(0, evictionCount - other.evictionCount));
220  }
221
222  /**
223   * Returns a new {@code CacheStats} representing the sum of this {@code CacheStats}
224   * and {@code other}.
225   *
226   * @since 11.0
227   */
228  public CacheStats plus(CacheStats other) {
229    return new CacheStats(
230        hitCount + other.hitCount,
231        missCount + other.missCount,
232        loadSuccessCount + other.loadSuccessCount,
233        loadExceptionCount + other.loadExceptionCount,
234        totalLoadTime + other.totalLoadTime,
235        evictionCount + other.evictionCount);
236  }
237
238  @Override
239  public int hashCode() {
240    return Objects.hashCode(hitCount, missCount, loadSuccessCount, loadExceptionCount,
241        totalLoadTime, evictionCount);
242  }
243
244  @Override
245  public boolean equals(@Nullable Object object) {
246    if (object instanceof CacheStats) {
247      CacheStats other = (CacheStats) object;
248      return hitCount == other.hitCount
249          && missCount == other.missCount
250          && loadSuccessCount == other.loadSuccessCount
251          && loadExceptionCount == other.loadExceptionCount
252          && totalLoadTime == other.totalLoadTime
253          && evictionCount == other.evictionCount;
254    }
255    return false;
256  }
257
258  @Override
259  public String toString() {
260    return Objects.toStringHelper(this)
261        .add("hitCount", hitCount)
262        .add("missCount", missCount)
263        .add("loadSuccessCount", loadSuccessCount)
264        .add("loadExceptionCount", loadExceptionCount)
265        .add("totalLoadTime", totalLoadTime)
266        .add("evictionCount", evictionCount)
267        .toString();
268  }
269}