Skip to content

Latest commit

 

History

History
94 lines (83 loc) · 8.36 KB

memorycache_poc_log.md

File metadata and controls

94 lines (83 loc) · 8.36 KB

Commit descriptions

This log describes the commits in branch memorycache-clockquantization-jit-now.

Groundwork

The first set of 4 commits lays the foundation for the eventual changes. They ensure that new temporal primitives and the new clock quantization primitives get introduced, while ensuring that existing tests leverage these newly introduced temporal primitives. Also, a new net5.0 target is introduced to ensure that we can leverage new features introduced in .NET 5.0.

  • Commit 64ec768 - temporal primitives:
    • Introduction of abstractions for system clock and temporal context, in preparation of clock quantization
    • Main interfaces: Microsoft.Extensions.Internal.ClockQuantization.ISystemClock, Microsoft.Extensions.Internal.ClockQuantization.ISystemClockTemporalContext
    • Implementation of shims around external clocks (incl. ManualClock for testing purposes), as well as a built-in TFM-optimized InternalClock
    • Factory method determines most appropriate shim based on traits of the provided Microsoft.Extensions.Internal.ISystemClock (may be nullInternalClock)
    • We may drop some specific shims based on insights gained along the way
  • Commit 74a49af - Microsoft.Extensions.Internal.TestClock reacting to temporal primitives:
    • Extends the existing TestClock with Microsoft.Extensions.Internal.ClockQuantization.ISystemClockTemporalContext traits (internal interface)
    • An internal ClockAdjusted event will be raised when the clock is adjusted in TestClock.Add()
    • Note: the original public API surface of TestClock was left untouched
  • Commit 66ae3e5 - additional references and unlocking TFM-specific capabilities for .NET 5.0+:
    • Adding net5.0 TFM to unlock Environment.TickCount64 for .NET 5.0 and .NET 6.0
    • TFM-specific references for net461:
      • System.Runtime.CompilerServices.Unsafe (Interlocked.* operations acting on uint)
      • System.Threading.Tasks.Extensions (ValueTask)
      • Microsoft.Bcl.AsyncInterfaces (IAsyncDisposable)
    • TFM-specific references for netstandard2.0
      • System.Threading.Tasks.Extensions (ValueTask)
      • Microsoft.Bcl.AsyncInterfaces (IAsyncDisposable)
    • TFM-specific references for net5.0
      • System.Runtime
      • System.Threading
      • System.Threading.ThreadPool
      • System.Collections
      • System.Collections.Concurrent
  • Commit 132ac84 - clock quantization primitives:
    • LazyClockOffsetSerialPosition
    • LazyClockOffsetSerialPosition.Snapshot
    • Interval
    • Interval.SnapshotTracker

Replacing the original clock

Expecting worse performance due to additional layers of abstraction and unit conversions. Main purpose is to exercise (most) clock access functions and unit conversion functions:

  • UtcNow
  • UtcNowClockOffset (the option that is faster in .NET 5.0+)
  • DateTimeOffsetToClockOffset()
  • ClockOffsetToUtcDateTimeOffset()
  • TimeSpanToClockOffsetUnits()
  • ClockOffsetUnitsToTimeSpan()

  • Commit 9376f4f - ClockQuantizer and TemporalContextDriver:
  • Commit aafd8b6 - adapt time-related CacheEntry properties and backing storage
    • Adapt time-related CacheEntry properties and backing storage to leverage LazyClockOffsetSerialPosition (clock offset plus serial position) and long (clock offset only)
      • LastAccessed (also introducing new property LastAccessedClockOffsetSerialPosition)
      • AbsoluteExpration
      • SlidingExpiration
    • Half-hearted approach to leave as much in tact as possible, hopefully making it easier to review commit diffs

Rewiring clock access, expiry checks and LRU bookkeeping/eviction

  • Commit 556a852 - optimize expiration scan trigger checks
    • Rewriting bookkeeping and checks in terms of clock offset (units)
    • If possible, ride the wave of an existing exact LazyClockOffsetSerialPosition; if that doesn't exist, fall back to the most inexpensive clock access method: UtcNowClockOffset
  • Commit 71fa1b0 - optimize TryGetValue() with interval-based expiration checks and LazyClockOffsetSerialPosition
    • Introduction of interval-based entry expiration checks
    • Most appropriate clock-offset-serial position determined based on entry type and temporal context; applied as late as possible (and only when needed)
    • One downside for entries with sliding expiration: if an entry has not yet expired, we incur a so-called "advance operation" (allocating!) to ensure proper expiration tracking, as well as proper LRU ordering of subsequent entry retrievals
  • Commit f8e0a12 - optimize SetEntry() with interval-based expiration checks and LazyClockOffsetSerialPosition
    • Upfront, obtain an exact clock-offset-serial position only for entries with sliding expiration; we incur a so-called "advance operation" (allocating!)
    • Be opportunistic in determining absolute expiration if "relative to now" absolute expiration is at play; if possible, ride the wave of an existing exact LazyClockOffsetSerialPosition; if that doesn't exist, fall back to the most inexpensive clock access method: UtcNowClockOffset
    • Leverage interval-based expiration checks introduced with commit 71fa1b0

Optimizing and code clean-up

  • Commit b803e28 - additional LazyClockOffsetSerialPosition capabilities
    • Public method AssignExactClockOffsetSerialPosition() to (re-)initialize an instance reference with an "exact" clock offset
    • Implement IComparable<T>
  • Commit 5a25899 - rationalize CacheEntry time-based properties & expiration checks
    • LastAccessed property removed (now fully superseded by LastAccessedClockOffsetSerialPosition)
    • Implement AbsoluteExpirationClockOffset as an internal field
    • Implement LastAccessedClockOffsetSerialPosition as an internal field
    • Implement SlidingExpirationClockOffsetUnits as an internal property (private set)
    • Introduction of CheckExpired(..., bool absoluteExpirationUndecided, bool slidingExpirationUndecided)
      • Allowing for more optimal use from places where we already know the values of these flags - e.g., in MemoryCache.SetEntry()
      • Existing CheckExpired() method rewired onto new method
    • DateTimeOffset-based expiration methods left in place for test purposes, but rewired onto the LazyClockOffsetSerialPosition-based implementations
  • Commit 7b5db3b - optimize MemoryCache
    • Further optimize SetEntry(); amongst others, leverage the newly introduced CacheEntry.CheckExpired(..., bool absoluteExpirationUndecided, bool slidingExpirationUndecided) method
    • Rewire MemoryCache.ScanForExpiredItems() onto the LazyClockOffsetSerialPosition-based expiration check
    • Rewire MemoryCache.Compact() onto the LazyClockOffsetSerialPosition-based expiration check
    • Rewire MemoryCache.Compact().ExpirePriorityBucket() onto the LazyClockOffsetSerialPosition custom comparer
  • Commit 70d15a2 - add missing #nullable pragmas