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