public void MetricsErrorCacheEntry_Simple() { try { throw new Exception(); } catch (Exception ex) { var entry = new MetricsErrorCacheEntry <int, int>(42, ex); Assert.AreEqual(42, entry.Key); Assert.AreSame(ex, entry.Exception); var stack = ex.StackTrace; try { _ = entry.Value; } catch (Exception ex2) { Assert.AreSame(ex, ex2); Assert.AreEqual(stack, ex2.StackTrace.Substring(0, stack.Length)); } Assert.AreEqual(0, entry.HitCount); Assert.AreEqual(0L, entry.CreationTime.Ticks); Assert.AreEqual(0L, entry.InvokeDuration.Ticks); Assert.AreEqual(0L, entry.LastAccessTime.Ticks); Assert.AreEqual(0L, entry.TotalDuration.Ticks); // // NB: AverageAccessTime and SpeedupFactor throw DivideByZeroException until values are // initialized. Accesses to those properties is guaranteed to happen after the hit // count has been incremented to 1, so this is not an issue. // Assert.ThrowsException <DivideByZeroException>(() => entry.AverageAccessTime); Assert.ThrowsException <DivideByZeroException>(() => entry.SpeedupFactor); entry.HitCount++; Assert.AreEqual(0L, entry.AverageAccessTime.Ticks); Assert.IsTrue(double.IsNaN(entry.SpeedupFactor)); // can only happen if access time is zero entry.TotalDuration = TimeSpan.FromTicks(27182818284590451L); Assert.AreEqual(27182818284590451L, entry.AverageAccessTime.Ticks); Assert.AreEqual(0.0, entry.SpeedupFactor); entry.InvokeDuration = TimeSpan.FromTicks(31415926535897931L); Assert.AreEqual(27182818284590451L, entry.AverageAccessTime.Ticks); Assert.AreEqual((double)31415926535897931L / 27182818284590451L, entry.SpeedupFactor); entry.HitCount++; Assert.AreEqual(27182818284590451L / 2, entry.AverageAccessTime.Ticks); Assert.AreEqual((double)31415926535897931L / (27182818284590451L / 2), entry.SpeedupFactor); var sb = new StringBuilder(); entry.ToDebugView(sb, ""); Assert.IsTrue(sb.ToString().Contains("=")); } }
public Cache(Func <T, R> function, Func <IMemoizationCacheEntryMetrics, TMetric> ranker, int maxCapacity, bool descending, double ageThreshold, bool cacheError, IStopwatchFactory stopwatchFactory) { _function = args => { var weakArgs = WeakReferenceExtensions.Create(args); var invokeDuration = default(TimeSpan); #if DEBUG Interlocked.Increment(ref _invocationCount); #endif Trim(); var res = default(IMetricsCacheEntry <WeakReference <T>, R>); try { var swInvokeStart = _stopwatch.ElapsedTicks; var value = function(args); invokeDuration = new TimeSpan(_stopwatch.ElapsedTicks - swInvokeStart); res = new MetricsValueCacheEntry <WeakReference <T>, R>(weakArgs, value); } catch (Exception ex) when(_cacheError) { res = new MetricsErrorCacheEntry <WeakReference <T>, R>(weakArgs, ex); } res.CreationTime = new TimeSpan(_stopwatch.ElapsedTicks); res.InvokeDuration = invokeDuration; _lock.EnterWriteLock(); try { _entries.Add(res); } finally { _lock.ExitWriteLock(); } return(res); }; _cache = new WeakCacheDictionary <T, IMetricsCacheEntry <WeakReference <T>, R> >(); _lock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion); _entries = new HashSet <IMetricsCacheEntry <WeakReference <T>, R> >(); _stopwatch = stopwatchFactory.StartNew(); // // Exclude newest items which have statically irrelevant data, so they get a chance to become relevant. // var candidates = _entries.OrderBy(e => _stopwatch.ElapsedTicks - e.CreationTime.Ticks).Take(Math.Max(1, (int)(maxCapacity * ageThreshold))); _ranker = descending ? candidates.OrderByDescending(e => ranker(e)) : candidates.OrderBy(e => ranker(e)); _maxCapacity = maxCapacity; _cacheError = cacheError; }
public Cache(Func <T, R> function, Func <IMemoizationCacheEntryMetrics, TMetric> ranker, int maxCapacity, bool descending, double ageThreshold, IEqualityComparer <T> comparer, bool cacheError, IStopwatchFactory stopwatchFactory) { _function = args => { var invokeDuration = default(TimeSpan); #if DEBUG _invocationCount++; #endif Trim(); var res = default(IMetricsCacheEntry <T, R>); try { var swInvokeStart = _stopwatch.ElapsedTicks; var value = function(args); invokeDuration = new TimeSpan(_stopwatch.ElapsedTicks - swInvokeStart); res = new MetricsValueCacheEntry <T, R>(args, value); } catch (Exception ex) when(_cacheError) { res = new MetricsErrorCacheEntry <T, R>(args, ex); } res.CreationTime = new TimeSpan(_stopwatch.ElapsedTicks); res.InvokeDuration = invokeDuration; return(res); }; _cache = new CacheDictionary <T, IMetricsCacheEntry <T, R> >(comparer); _stopwatch = stopwatchFactory.StartNew(); // // Exclude newest items which have statically irrelevant data, so they get a chance to become relevant. // var candidates = _cache.Values.OrderBy(e => _stopwatch.ElapsedTicks - e.CreationTime.Ticks).Take(Math.Max(1, (int)(maxCapacity * ageThreshold))); _ranker = descending ? candidates.OrderByDescending(e => ranker(e)) : candidates.OrderBy(e => ranker(e)); _maxCapacity = maxCapacity; _cacheError = cacheError; }