-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
14 changed files
with
2,483 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
|
||
Microsoft Visual Studio Solution File, Format Version 12.00 | ||
# Visual Studio Version 16 | ||
VisualStudioVersion = 16.0.30804.86 | ||
MinimumVisualStudioVersion = 10.0.40219.1 | ||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ClockQuantization", "src\ClockQuantization.csproj", "{CB1FF5D7-446B-44D0-B9B3-DE204F67FC3C}" | ||
EndProject | ||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{7406849D-980E-42B0-8BCE-FBDAB80362C5}" | ||
ProjectSection(SolutionItems) = preProject | ||
readme.md = readme.md | ||
EndProjectSection | ||
EndProject | ||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClockQuantization.Tests", "tests\ClockQuantization.Tests\ClockQuantization.Tests.csproj", "{7C538D0C-2B25-481A-92C8-130A20336C6F}" | ||
EndProject | ||
Global | ||
GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||
Debug|Any CPU = Debug|Any CPU | ||
Release|Any CPU = Release|Any CPU | ||
EndGlobalSection | ||
GlobalSection(ProjectConfigurationPlatforms) = postSolution | ||
{CB1FF5D7-446B-44D0-B9B3-DE204F67FC3C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||
{CB1FF5D7-446B-44D0-B9B3-DE204F67FC3C}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||
{CB1FF5D7-446B-44D0-B9B3-DE204F67FC3C}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||
{CB1FF5D7-446B-44D0-B9B3-DE204F67FC3C}.Release|Any CPU.Build.0 = Release|Any CPU | ||
{7C538D0C-2B25-481A-92C8-130A20336C6F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||
{7C538D0C-2B25-481A-92C8-130A20336C6F}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||
{7C538D0C-2B25-481A-92C8-130A20336C6F}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||
{7C538D0C-2B25-481A-92C8-130A20336C6F}.Release|Any CPU.Build.0 = Release|Any CPU | ||
EndGlobalSection | ||
GlobalSection(SolutionProperties) = preSolution | ||
HideSolutionNode = FALSE | ||
EndGlobalSection | ||
GlobalSection(ExtensibilityGlobals) = postSolution | ||
SolutionGuid = {4D8B842C-B349-4411-84A3-55C62613E60E} | ||
EndGlobalSection | ||
EndGlobal |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
# Introduction | ||
|
||
Clock Quantization is proposed as a method to decrease the frequency of determining the exact time, while still being | ||
able to make time-dependent decisions. | ||
|
||
Consider an <i>interval</i> defined as a time span for which we register a "start" `DateTimeOffset` and keep an internal | ||
<i>serial position</i>. The latter is incremented every time that we issue a new so-called <i>time-serial position</i>. Every time | ||
that such <i>time-serial position</i> is created off said <i>interval</i>, it will assume two properties: | ||
1. A `DateTimeOffset` which is the copy of the interval's `DateTimeOffset` | ||
2. A `SerialPosition` which is a copy of the interval's internal serial position at the time of issuance. | ||
|
||
An internal "metronome" ensures that a new interval is periodically started after `MaxIntervalTimeSpan` has passed. | ||
|
||
The result: | ||
* The continuous clock is quantized into regular intervals with known maximum length, allowing system calls (e.g. `DateTimeOffset.UtcNow`) | ||
to be required less frequent and amortized across multiple operations. | ||
* The status of "events" that occur <u>outside</u> of the interval can be reasoned about with absolute certainty. E.g., taking `interval` as a reference frame: | ||
* A cache item that expires before `interval.DateTimeOffset` will definitely have expired | ||
* A cache item that expires after `interval.DateTimeOffset + MaxIntervalTimeSpan` will definitely expire in the future (i.e. it has | ||
not expired yet) | ||
* The status of "events" that occur <u>inside</u> the interval is in doubt, but policies could define how to deal with that. E.g., | ||
with the same example of cache item expiration, one of the following policies could be applied: | ||
* Precise → fall back to determining a precise `DateTimeOffset`, incurring a call onto the clock and applying the "normal" logic. | ||
* Optimistic → just consider the item not expired yet | ||
* Pessimistic → just consider the item as already expired | ||
* The amount of uncertainty (i.e. in-doubt "events") becomes a function of `MaxIntervalTimeSpan`. The smaller `MaxIntervalTimeSpan`, | ||
the smaller the amount of uncertainty (and number of times that we still need to regress to calling onto the clock to determine | ||
the exact time). | ||
* The <i>time-serial positions</i> within an interval can be ordered by their `SerialPosition` property, still allowing for e.g. | ||
strict LRU orderering. Alternatively, it also makes it possible to apply LRU ordering to a <i>cluster</i> of cache entries (all having | ||
equal `LastAccessed.DateTimeOffset` - now also a <i>time-serial position</i>). | ||
|
||
# Context | ||
This repo stems from additional research after I posted a [comment](https://github.com/dotnet/runtime/pull/45842#issuecomment-742100677) in PR [dotnet/runtime#45842](https://github.com/dotnet/runtime/pull/45842). In that comment, I did not take into | ||
account the fact that `CacheEntry.LastAccessed` is often updated with `DateTimeOffset.UtcNow` in order to support: | ||
* Sliding expiration | ||
* LRU-based cache eviction during cache compaction | ||
|
||
After some local experimentation with `Lazy<T>` (as suchested in my initial comment) and | ||
[`LazyInitializer.EnsureInitialized()`](https://docs.microsoft.com/en-us/dotnet/api/system.threading.lazyinitializer.ensureinitialized?view=net-5.0#System_Threading_LazyInitializer_EnsureInitialized__1___0__System_Boolean__System_Object__System_Func___0__), | ||
I did get some promising results, but realized that it resulted in some quite convoluted code. Hence, I decided to first create and | ||
share an abstraction that reflects the idea behind a potential direction for further optimization. | ||
|
||
# Remarks | ||
* The `ISystemClockTemporalContext` abstraction introduced as part of this concept might be useful in other scenarios where a | ||
synthetic clock and/or timer must be imposed onto a subject/system (e.g. event replay, unit tests). | ||
* The solution in this repo contains a test project. It serves two purposes: | ||
1. Ensure that I didn't leave too many gaps | ||
2. Document the idea behind `ISystemClockTemporalContext`, without actually writing documentation | ||
|
||
# Remaining work | ||
- [ ] Implement `IDisposable` and/or `IAsyncDisposable` on `ClockQuantizer` | ||
- [ ] Integrate this proposal in a [local fork](https://github.com/edevoogd/runtime) of Microsoft.Extensions.Caching.Memory as a PoC |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<OutputType>Library</OutputType> | ||
<LangVersion>9.0</LangVersion> | ||
<TargetFrameworks>netstandard2.0;net50</TargetFrameworks> | ||
<Nullable>enable</Nullable> | ||
</PropertyGroup> | ||
|
||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net50|AnyCPU'"> | ||
<DocumentationFile>.\ClockQuantization.xml</DocumentationFile> | ||
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies> | ||
</PropertyGroup> | ||
|
||
<ItemGroup Condition="'$(TargetFramework)|$(Platform)'=='netstandard2.0|AnyCPU'"> | ||
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="5.0.0" /> | ||
</ItemGroup> | ||
|
||
</Project> |
Oops, something went wrong.