Skip to content

Commit

Permalink
Switched to ValueTask for CacheStack and extensions
Browse files Browse the repository at this point in the history
  • Loading branch information
Turnerj committed Oct 27, 2019
1 parent f9bcb60 commit 15726f1
Show file tree
Hide file tree
Showing 14 changed files with 54 additions and 50 deletions.
24 changes: 12 additions & 12 deletions docs/Performance.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,18 @@ Job=Core Runtime=Core

| Method | Mean [ns] | Error [ns] | StdDev [ns] | Gen 0 | Gen 1 | Gen 2 | Allocated [B] |
|-------------------------- |----------------:|--------------:|--------------:|-------:|------:|------:|--------------:|
| SetupAndTeardown | 284.8 ns | 2.39 ns | 2.12 ns | 0.1326 | - | - | 416 B |
| Set | 700.3 ns | 16.63 ns | 17.79 ns | 0.2108 | - | - | 664 B |
| Set_TwoLayers | 748.5 ns | 5.08 ns | 4.75 ns | 0.2804 | - | - | 880 B |
| Evict | 765.4 ns | 4.47 ns | 4.18 ns | 0.2108 | - | - | 664 B |
| Evict_TwoLayers | 923.5 ns | 3.78 ns | 3.54 ns | 0.2804 | - | - | 880 B |
| Cleanup | 981.1 ns | 6.33 ns | 5.61 ns | 0.2098 | - | - | 664 B |
| Cleanup_TwoLayers | 1,189.7 ns | 15.05 ns | 13.34 ns | 0.2804 | - | - | 880 B |
| GetMiss | 393.9 ns | 3.29 ns | 3.08 ns | 0.1326 | - | - | 416 B |
| GetHit | 817.5 ns | 8.24 ns | 6.88 ns | 0.2346 | - | - | 736 B |
| GetOrSet | 2,004.4 ns | 20.28 ns | 18.97 ns | 0.4768 | - | - | 1504 B |
| GetOrSet_TwoSimultaneous | 62,133,010.3 ns | 415,745.68 ns | 368,547.72 ns | - | - | - | 2720 B |
| GetOrSet_FourSimultaneous | 62,121,809.2 ns | 478,958.15 ns | 448,017.75 ns | - | - | - | 4565 B |
| SetupAndTeardown | 275.0 ns | 1.30 ns | 1.22 ns | 0.1326 | - | - | 416 B |
| Set | 678.8 ns | 3.35 ns | 3.13 ns | 0.1879 | - | - | 592 B |
| Set_TwoLayers | 773.4 ns | 2.06 ns | 1.93 ns | 0.2575 | - | - | 808 B |
| Evict | 790.9 ns | 3.48 ns | 3.25 ns | 0.1879 | - | - | 592 B |
| Evict_TwoLayers | 941.2 ns | 4.91 ns | 4.60 ns | 0.2575 | - | - | 808 B |
| Cleanup | 974.4 ns | 7.65 ns | 7.16 ns | 0.1869 | - | - | 592 B |
| Cleanup_TwoLayers | 1,208.6 ns | 4.53 ns | 3.54 ns | 0.2575 | - | - | 808 B |
| GetMiss | 388.1 ns | 2.34 ns | 2.07 ns | 0.1326 | - | - | 416 B |
| GetHit | 800.7 ns | 5.10 ns | 4.52 ns | 0.1879 | - | - | 592 B |
| GetOrSet | 2,043.9 ns | 14.51 ns | 13.58 ns | 0.3357 | - | - | 1064 B |
| GetOrSet_TwoSimultaneous | 62,232,054.8 ns | 329,492.60 ns | 308,207.58 ns | - | - | - | 3325 B |
| GetOrSet_FourSimultaneous | 62,144,815.6 ns | 395,286.63 ns | 369,751.36 ns | - | - | - | 3677 B |

## Cache Layer Comparison Benchmark

Expand Down
2 changes: 1 addition & 1 deletion src/CacheTower.Extensions.Redis/RedisLockExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public void Register(ICacheStack cacheStack)
RegisteredStack = cacheStack;
}

public async Task<CacheEntry<T>> RefreshValueAsync<T>(string cacheKey, Func<Task<CacheEntry<T>>> valueProvider, CacheSettings settings)
public async ValueTask<CacheEntry<T>> RefreshValueAsync<T>(string cacheKey, Func<ValueTask<CacheEntry<T>>> valueProvider, CacheSettings settings)
{
var hasLock = await Database.StringSetAsync(cacheKey, RedisValue.EmptyString, expiry: LockTimeout, when: When.NotExists);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public RedisRemoteEvictionExtension(ConnectionMultiplexer connection, string cha
FlaggedRefreshes = new HashSet<string>(StringComparer.Ordinal);
}

public async Task OnValueRefreshAsync(string cacheKey, TimeSpan timeToLive)
public async ValueTask OnValueRefreshAsync(string cacheKey, TimeSpan timeToLive)
{
lock (FlaggedRefreshesLockObj)
{
Expand Down
20 changes: 10 additions & 10 deletions src/CacheTower/CacheStack.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ private void ThrowIfDisposed()
}
}

public async Task CleanupAsync()
public async ValueTask CleanupAsync()
{
ThrowIfDisposed();

Expand All @@ -70,7 +70,7 @@ public async Task CleanupAsync()
}
}

public async Task EvictAsync(string cacheKey)
public async ValueTask EvictAsync(string cacheKey)
{
ThrowIfDisposed();

Expand All @@ -93,7 +93,7 @@ public async Task EvictAsync(string cacheKey)
}
}

public async Task<CacheEntry<T>> SetAsync<T>(string cacheKey, T value, TimeSpan timeToLive)
public async ValueTask<CacheEntry<T>> SetAsync<T>(string cacheKey, T value, TimeSpan timeToLive)
{
ThrowIfDisposed();

Expand All @@ -102,7 +102,7 @@ public async Task<CacheEntry<T>> SetAsync<T>(string cacheKey, T value, TimeSpan
return cacheEntry;
}

public async Task SetAsync<T>(string cacheKey, CacheEntry<T> cacheEntry)
public async ValueTask SetAsync<T>(string cacheKey, CacheEntry<T> cacheEntry)
{
ThrowIfDisposed();

Expand All @@ -116,7 +116,7 @@ public async Task SetAsync<T>(string cacheKey, CacheEntry<T> cacheEntry)
throw new ArgumentNullException(nameof(cacheEntry));
}

for (int i = 0, l = CacheLayers.Length; i < l; i += 2)
for (int i = 0, l = CacheLayers.Length; i < l; i++)
{
var layer = CacheLayers[i];
if (layer is ISyncCacheLayer syncLayerOne)
Expand All @@ -130,7 +130,7 @@ public async Task SetAsync<T>(string cacheKey, CacheEntry<T> cacheEntry)
}
}

public async Task<CacheEntry<T>> GetAsync<T>(string cacheKey)
public async ValueTask<CacheEntry<T>> GetAsync<T>(string cacheKey)
{
ThrowIfDisposed();

Expand Down Expand Up @@ -171,7 +171,7 @@ public async Task<CacheEntry<T>> GetAsync<T>(string cacheKey)
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private async Task<(int LayerIndex, CacheEntry<T> CacheEntry)> GetWithLayerIndexAsync<T>(string cacheKey)
private async ValueTask<(int LayerIndex, CacheEntry<T> CacheEntry)> GetWithLayerIndexAsync<T>(string cacheKey)
{
for (var layerIndex = 0; layerIndex < CacheLayers.Length; layerIndex++)
{
Expand Down Expand Up @@ -204,7 +204,7 @@ public async Task<CacheEntry<T>> GetAsync<T>(string cacheKey)
return default;
}

public async Task<T> GetOrSetAsync<T>(string cacheKey, Func<T, ICacheContext, Task<T>> getter, CacheSettings settings)
public async ValueTask<T> GetOrSetAsync<T>(string cacheKey, Func<T, ICacheContext, Task<T>> getter, CacheSettings settings)
{
ThrowIfDisposed();

Expand Down Expand Up @@ -256,7 +256,7 @@ public async Task<T> GetOrSetAsync<T>(string cacheKey, Func<T, ICacheContext, Ta
}
}

private async Task BackPopulateCacheAsync<T>(int fromIndexExclusive, string cacheKey, CacheEntry<T> cacheEntry)
private async ValueTask BackPopulateCacheAsync<T>(int fromIndexExclusive, string cacheKey, CacheEntry<T> cacheEntry)
{
ThrowIfDisposed();

Expand Down Expand Up @@ -305,7 +305,7 @@ private async Task BackPopulateCacheAsync<T>(int fromIndexExclusive, string cach
}
}

private async Task<CacheEntry<T>> RefreshValueAsync<T>(string cacheKey, Func<T, ICacheContext, Task<T>> getter, CacheSettings settings, bool waitForRefresh)
private async ValueTask<CacheEntry<T>> RefreshValueAsync<T>(string cacheKey, Func<T, ICacheContext, Task<T>> getter, CacheSettings settings, bool waitForRefresh)
{
ThrowIfDisposed();

Expand Down
1 change: 1 addition & 0 deletions src/CacheTower/CacheTower.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
<ItemGroup>
<PackageReference Include="Nito.AsyncEx" Version="5.0.0" />
<PackageReference Include="System.Buffers" Version="4.5.0" />
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.3" />
</ItemGroup>

</Project>
4 changes: 2 additions & 2 deletions src/CacheTower/Extensions/ExtensionContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public void Register(ICacheStack cacheStack)
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Task<CacheEntry<T>> RefreshValueAsync<T>(string cacheKey, Func<Task<CacheEntry<T>>> valueProvider, CacheSettings settings)
public ValueTask<CacheEntry<T>> RefreshValueAsync<T>(string cacheKey, Func<ValueTask<CacheEntry<T>>> valueProvider, CacheSettings settings)
{
if (!HasRefreshWrapperExtension)
{
Expand All @@ -76,7 +76,7 @@ public Task<CacheEntry<T>> RefreshValueAsync<T>(string cacheKey, Func<Task<Cache
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public async Task OnValueRefreshAsync(string cacheKey, TimeSpan timeToLive)
public async ValueTask OnValueRefreshAsync(string cacheKey, TimeSpan timeToLive)
{
if (HasValueRefreshExtensions)
{
Expand Down
4 changes: 2 additions & 2 deletions src/CacheTower/ICacheExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ public interface ICacheExtension

public interface IValueRefreshExtension : ICacheExtension
{
Task OnValueRefreshAsync(string cacheKey, TimeSpan timeToLive);
ValueTask OnValueRefreshAsync(string cacheKey, TimeSpan timeToLive);
}

public interface IRefreshWrapperExtension : ICacheExtension
{
Task<CacheEntry<T>> RefreshValueAsync<T>(string cacheKey, Func<Task<CacheEntry<T>>> valueProvider, CacheSettings settings);
ValueTask<CacheEntry<T>> RefreshValueAsync<T>(string cacheKey, Func<ValueTask<CacheEntry<T>>> valueProvider, CacheSettings settings);
}
}
12 changes: 6 additions & 6 deletions src/CacheTower/ICacheStack.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ namespace CacheTower
{
public interface ICacheStack
{
Task CleanupAsync();
Task EvictAsync(string cacheKey);
Task<CacheEntry<T>> SetAsync<T>(string cacheKey, T value, TimeSpan timeToLive);
Task SetAsync<T>(string cacheKey, CacheEntry<T> cacheEntry);
Task<CacheEntry<T>> GetAsync<T>(string cacheKey);
Task<T> GetOrSetAsync<T>(string cacheKey, Func<T, ICacheContext, Task<T>> getter, CacheSettings settings);
ValueTask CleanupAsync();
ValueTask EvictAsync(string cacheKey);
ValueTask<CacheEntry<T>> SetAsync<T>(string cacheKey, T value, TimeSpan timeToLive);
ValueTask SetAsync<T>(string cacheKey, CacheEntry<T> cacheEntry);
ValueTask<CacheEntry<T>> GetAsync<T>(string cacheKey);
ValueTask<T> GetOrSetAsync<T>(string cacheKey, Func<T, ICacheContext, Task<T>> getter, CacheSettings settings);
}
}
8 changes: 6 additions & 2 deletions tests/CacheTower.Benchmarks/CacheStackBenchmark.cs
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,8 @@ public async Task GetOrSet_TwoSimultaneous()
return 12;
}, new CacheSettings(TimeSpan.FromDays(1)));

await Task.WhenAll(task1, task2);
await task1;
await task2;
}
}
[Benchmark]
Expand Down Expand Up @@ -164,7 +165,10 @@ public async Task GetOrSet_FourSimultaneous()
return 12;
}, new CacheSettings(TimeSpan.FromDays(1)));

await Task.WhenAll(task1, task2, task3, task4);
await task1;
await task2;
await task3;
await task4;
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public async Task RefreshValue()
extension.Register(CacheStack);
await extension.RefreshValueAsync<int>("RefreshValue", () =>
{
return Task.FromResult(new CacheEntry<int>(5, DateTime.UtcNow, TimeSpan.FromDays(1)));
return new ValueTask<CacheEntry<int>>(new CacheEntry<int>(5, DateTime.UtcNow, TimeSpan.FromDays(1)));
}, new CacheSettings(TimeSpan.FromDays(1)));
await DisposeOf(extension);
}
Expand Down
4 changes: 2 additions & 2 deletions tests/CacheTower.Tests/CacheStackStressTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public async Task SimulatenousGetOrSet_CacheMiss(int iterations)
return index;
}, new CacheSettings(TimeSpan.FromDays(1)));

allTasks.Add(iterationTask);
allTasks.Add(iterationTask.AsTask());
}

if (stopwatch.Elapsed.TotalSeconds > 10)
Expand Down Expand Up @@ -75,7 +75,7 @@ public async Task SimulatenousGetOrSet_CacheMiss_UniqueKeys(int iterations)
return index + 1;
}, new CacheSettings(TimeSpan.FromDays(1)));

allTasks.Add(iterationTask);
allTasks.Add(iterationTask.AsTask());
}

var lastTaskResult = await allTasks[iterations - 1];
Expand Down
2 changes: 1 addition & 1 deletion tests/CacheTower.Tests/CacheStackTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ public async Task GetOrSet_ConcurrentStaleCacheHits()
var cacheEntry = new CacheEntry<int>(23, DateTime.UtcNow.AddDays(-3), TimeSpan.FromDays(1));
await cacheStack.SetAsync("GetOrSet_ConcurrentStaleCacheHits", cacheEntry);

Task<int> DoRequest()
ValueTask<int> DoRequest()
{
return cacheStack.GetOrSetAsync<int>("GetOrSet_ConcurrentStaleCacheHits", async (oldValue, context) =>
{
Expand Down
4 changes: 2 additions & 2 deletions tests/CacheTower.Tests/Extensions/ExtensionContainerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,13 @@ public async Task RefreshWrapperExtension()

var refreshedValue = await container.RefreshValueAsync("WrapperTestCacheKey", () =>
{
return Task.FromResult(cacheEntry);
return new ValueTask<CacheEntry<int>>(cacheEntry);
}, new CacheSettings(TimeSpan.FromDays(1)));

refreshWrapperMock.Verify(e => e.Register(cacheStackMock.Object), Times.Once);
refreshWrapperMock.Verify(e => e.RefreshValueAsync(
"WrapperTestCacheKey",
It.IsAny<Func<Task<CacheEntry<int>>>>(), new CacheSettings(TimeSpan.FromDays(1))
It.IsAny<Func<ValueTask<CacheEntry<int>>>>(), new CacheSettings(TimeSpan.FromDays(1))
),
Times.Once
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,7 @@ await connection.GetSubscriber().SubscribeAsync("CacheTower.CacheLock", (channel
var cacheEntry = new CacheEntry<int>(13, DateTime.UtcNow, TimeSpan.FromDays(1));

await extension.RefreshValueAsync("TestKey",
() => Task.FromResult(cacheEntry), new CacheSettings(TimeSpan.FromDays(1)));

() => new ValueTask<CacheEntry<int>>(cacheEntry), new CacheSettings(TimeSpan.FromDays(1)));

var waitTask = taskCompletionSource.Task;
await Task.WhenAny(waitTask, Task.Delay(TimeSpan.FromSeconds(10)));
Expand Down Expand Up @@ -132,17 +131,17 @@ public async Task WaitingTaskInSameInstanceUnlocksAndCompletes()
return cacheEntry;
},
new CacheSettings(TimeSpan.FromDays(1))
);
).AsTask();

await secondaryTaskKickoff.Task;

var secondaryTask = extension.RefreshValueAsync("TestKey",
() =>
{
return Task.FromResult(cacheEntry);
return new ValueTask<CacheEntry<int>>(cacheEntry);
},
new CacheSettings(TimeSpan.FromDays(1))
);
).AsTask();

var succeedingTask = await Task.WhenAny(primaryTask, secondaryTask);
Assert.AreEqual(await primaryTask, await succeedingTask, "Processing task call didn't complete first - something has gone very wrong.");
Expand Down Expand Up @@ -178,17 +177,17 @@ public async Task WaitingTaskInDifferentInstanceUnlocksAndCompletes()
return cacheEntry;
},
new CacheSettings(TimeSpan.FromDays(1))
);
).AsTask();

await secondaryTaskKickoff.Task;

var secondaryTask = extensionTwo.RefreshValueAsync("TestKey",
() =>
{
return Task.FromResult(cacheEntry);
return new ValueTask<CacheEntry<int>>(cacheEntry);
},
new CacheSettings(TimeSpan.FromDays(1))
);
).AsTask();

var succeedingTask = await Task.WhenAny(primaryTask, secondaryTask);
Assert.AreEqual(await primaryTask, await succeedingTask, "Processing task call didn't complete first - something has gone very wrong.");
Expand Down

0 comments on commit 15726f1

Please sign in to comment.