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 >= 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 }