Skip to content

Commit 23584fe

Browse files
authored
Merge branch 'main' into users/alexpeck/avx2
2 parents 91eb568 + fb787c1 commit 23584fe

File tree

6 files changed

+92
-32
lines changed

6 files changed

+92
-32
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
cls
2+
3+
dotnet BitFaster.Caching.ThroughputAnalysis.dll 1 100
4+
dotnet BitFaster.Caching.ThroughputAnalysis.dll 1 10000
5+
dotnet BitFaster.Caching.ThroughputAnalysis.dll 1 1000000
6+
dotnet BitFaster.Caching.ThroughputAnalysis.dll 1 10000000
7+
8+
dotnet BitFaster.Caching.ThroughputAnalysis.dll 2 1000
9+
dotnet BitFaster.Caching.ThroughputAnalysis.dll 2 100000
10+
dotnet BitFaster.Caching.ThroughputAnalysis.dll 2 10000000
11+
dotnet BitFaster.Caching.ThroughputAnalysis.dll 2 100000000
12+
13+
dotnet BitFaster.Caching.ThroughputAnalysis.dll 3 100
14+
dotnet BitFaster.Caching.ThroughputAnalysis.dll 3 10000
15+
dotnet BitFaster.Caching.ThroughputAnalysis.dll 3 1000000
16+
dotnet BitFaster.Caching.ThroughputAnalysis.dll 3 10000000
17+
18+
dotnet BitFaster.Caching.ThroughputAnalysis.dll 4 100
19+
dotnet BitFaster.Caching.ThroughputAnalysis.dll 4 10000
20+
dotnet BitFaster.Caching.ThroughputAnalysis.dll 4 1000000
21+
dotnet BitFaster.Caching.ThroughputAnalysis.dll 4 10000000

BitFaster.Caching.ThroughputAnalysis/ThroughputBenchmark.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public abstract class ThroughputBenchmarkBase
3131
Initialize?.Invoke(cache);
3232

3333
// Warmup a few times before estimating run time
34-
config.Iterations = 10;
34+
config.Iterations = 1;
3535

3636
for (int i = 0; i < warmup; i++)
3737
{
@@ -46,14 +46,17 @@ public abstract class ThroughputBenchmarkBase
4646
var sw = Stopwatch.StartNew();
4747
Run(Stage.Pilot, 0, threads, config, cache);
4848

49-
valid = sw.Elapsed > TimeSpan.FromMilliseconds(400) ? valid + 1 : 0;
49+
valid = sw.Elapsed > TimeSpan.FromMilliseconds(800) ? valid + 1 : 0;
5050

5151
if (valid > 3)
5252
{
5353
break;
5454
}
5555

56-
config.Iterations = (int)(1.2 * config.Iterations);
56+
if (valid == 0)
57+
{
58+
config.Iterations = config.Iterations < 5 ? config.Iterations + 1 : (int)(1.2 * config.Iterations);
59+
}
5760
}
5861

5962
int runCounter = 0;

BitFaster.Caching.UnitTests/Lfu/ConcurrentTLfuTests.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Runtime.InteropServices;
3+
using System.Threading;
34
using BitFaster.Caching.Lfu;
45
using BitFaster.Caching.Scheduler;
56
using BitFaster.Caching.UnitTests.Retry;
@@ -25,6 +26,36 @@ public ConcurrentTLfuTests()
2526
lfu = new ConcurrentTLfu<int, string>(capacity, new ExpireAfterWrite<int, string>(timeToLive));
2627
}
2728

29+
// This is a scenario test to verify maintenance is run promptly after read.
30+
[RetryFact]
31+
public void WhenItemIsAccessedTimeToExpireIsUpdated()
32+
{
33+
var cache = new ConcurrentLfuBuilder<int, int>()
34+
.WithCapacity(10)
35+
.WithExpireAfterAccess(TimeSpan.FromSeconds(5))
36+
.Build();
37+
38+
Timed.Execute(
39+
cache,
40+
cache =>
41+
{
42+
cache.AddOrUpdate(1, 1);
43+
return cache;
44+
},
45+
TimeSpan.FromSeconds(4),
46+
cache =>
47+
{
48+
cache.TryGet(1, out var value);
49+
},
50+
TimeSpan.FromSeconds(2),
51+
cache =>
52+
{
53+
cache.TryGet(1, out var value).Should().BeTrue();
54+
cache.TryGet(1, out value).Should().BeTrue();
55+
}
56+
);
57+
}
58+
2859
[Fact]
2960
public void ConstructAddAndRetrieveWithCustomComparerReturnsValue()
3061
{

BitFaster.Caching/BitFaster.Caching.csproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
<PackageLicenseFile>LICENSE</PackageLicenseFile>
1111
<PackageReadmeFile>ReadMe.md</PackageReadmeFile>
1212
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
13-
<Version>2.5.0</Version>
13+
<Version>2.5.1</Version>
1414
<Copyright>Copyright © Alex Peck $([System.DateTime]::Now.ToString(yyyy))</Copyright>
1515
<PackageProjectUrl></PackageProjectUrl>
1616
<RepositoryUrl>https://github.com/bitfaster/BitFaster.Caching</RepositoryUrl>
@@ -22,8 +22,8 @@
2222
<IncludeSymbols>True</IncludeSymbols>
2323
<EmbedUntrackedSources>true</EmbedUntrackedSources>
2424
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
25-
<AssemblyVersion>2.5.0.0</AssemblyVersion>
26-
<FileVersion>2.5.0.0</FileVersion>
25+
<AssemblyVersion>2.5.1.0</AssemblyVersion>
26+
<FileVersion>2.5.1.0</FileVersion>
2727
<GenerateDocumentationFile>true</GenerateDocumentationFile>
2828
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
2929
<IsTrimmable>true</IsTrimmable>

BitFaster.Caching/Lfu/ConcurrentLfuCore.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,7 @@ private bool TryGetImpl(K key, [MaybeNullWhen(false)] out V value)
276276
{
277277
TryScheduleDrain();
278278
}
279+
this.policy.OnRead(node);
279280
value = node.Value;
280281
return true;
281282
}
@@ -366,6 +367,7 @@ public bool TryUpdate(K key, V value)
366367
// and we will just lose ordering/hit count, but not orphan the node.
367368
this.writeBuffer.TryAdd(node);
368369
TryScheduleDrain();
370+
this.policy.OnWrite(node);
369371
return true;
370372
}
371373

@@ -525,8 +527,6 @@ private bool Maintenance(N? droppedWrite = null)
525527
{
526528
this.drainStatus.VolatileWrite(DrainStatus.ProcessingToIdle);
527529

528-
policy.AdvanceTime();
529-
530530
// Note: this is only Span on .NET Core 3.1+, else this is no-op and it is still an array
531531
var buffer = this.drainBuffer.AsSpanOrArray();
532532

@@ -601,7 +601,7 @@ private void OnAccess(N node)
601601
break;
602602
}
603603

604-
policy.OnRead(node);
604+
policy.AfterRead(node);
605605
}
606606

607607
private void OnWrite(N node)
@@ -650,7 +650,7 @@ private void OnWrite(N node)
650650
break;
651651
}
652652

653-
policy.OnWrite(node);
653+
policy.AfterWrite(node);
654654
}
655655

656656
private void PromoteProbation(LfuNode<K, V> node)

BitFaster.Caching/Lfu/NodePolicy.cs

Lines changed: 27 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@ internal interface INodePolicy<K, V, N>
1010
{
1111
N Create(K key, V value);
1212
bool IsExpired(N node);
13-
void AdvanceTime();
1413
void OnRead(N node);
1514
void OnWrite(N node);
15+
void AfterRead(N node);
16+
void AfterWrite(N node);
1617
void OnEvict(N node);
1718
void ExpireEntries<P>(ref ConcurrentLfuCore<K, V, N, P> cache) where P : struct, INodePolicy<K, V, N>;
1819
}
@@ -33,17 +34,22 @@ public bool IsExpired(AccessOrderNode<K, V> node)
3334
}
3435

3536
[MethodImpl(MethodImplOptions.AggressiveInlining)]
36-
public void AdvanceTime()
37+
public void OnRead(AccessOrderNode<K, V> node)
3738
{
3839
}
3940

4041
[MethodImpl(MethodImplOptions.AggressiveInlining)]
41-
public void OnRead(AccessOrderNode<K, V> node)
42+
public void OnWrite(AccessOrderNode<K, V> node)
4243
{
4344
}
4445

4546
[MethodImpl(MethodImplOptions.AggressiveInlining)]
46-
public void OnWrite(AccessOrderNode<K, V> node)
47+
public void AfterRead(AccessOrderNode<K, V> node)
48+
{
49+
}
50+
51+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
52+
public void AfterWrite(AccessOrderNode<K, V> node)
4753
{
4854
}
4955

@@ -84,29 +90,33 @@ public TimeOrderNode<K, V> Create(K key, V value)
8490

8591
[MethodImpl(MethodImplOptions.AggressiveInlining)]
8692
public bool IsExpired(TimeOrderNode<K, V> node)
87-
{
88-
return node.TimeToExpire < Duration.SinceEpoch();
89-
}
90-
91-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
92-
public void AdvanceTime()
9393
{
9494
current = Duration.SinceEpoch();
95+
return node.TimeToExpire < current;
9596
}
9697

9798
[MethodImpl(MethodImplOptions.AggressiveInlining)]
9899
public void OnRead(TimeOrderNode<K, V> node)
99100
{
100-
var oldTte = node.TimeToExpire;
101+
// we know IsExpired is always called immediate before OnRead, so piggyback on the current time
101102
node.TimeToExpire = current + expiryCalculator.GetExpireAfterRead(node.Key, node.Value, node.TimeToExpire - current);
102-
if (oldTte.raw != node.TimeToExpire.raw)
103-
{
104-
wheel.Reschedule(node);
105-
}
106103
}
107104

108105
[MethodImpl(MethodImplOptions.AggressiveInlining)]
109106
public void OnWrite(TimeOrderNode<K, V> node)
107+
{
108+
var current = Duration.SinceEpoch();
109+
node.TimeToExpire = current + expiryCalculator.GetExpireAfterUpdate(node.Key, node.Value, node.TimeToExpire - current);
110+
}
111+
112+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
113+
public void AfterRead(TimeOrderNode<K, V> node)
114+
{
115+
wheel.Reschedule(node);
116+
}
117+
118+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
119+
public void AfterWrite(TimeOrderNode<K, V> node)
110120
{
111121
// if the node is not yet scheduled, it is being created
112122
// the time is set on create in case it is read before the buffer is processed
@@ -116,12 +126,7 @@ public void OnWrite(TimeOrderNode<K, V> node)
116126
}
117127
else
118128
{
119-
var oldTte = node.TimeToExpire;
120-
node.TimeToExpire = current + expiryCalculator.GetExpireAfterUpdate(node.Key, node.Value, node.TimeToExpire - current);
121-
if (oldTte.raw != node.TimeToExpire.raw)
122-
{
123-
wheel.Reschedule(node);
124-
}
129+
wheel.Reschedule(node);
125130
}
126131
}
127132

@@ -134,7 +139,7 @@ public void OnEvict(TimeOrderNode<K, V> node)
134139
[MethodImpl(MethodImplOptions.AggressiveInlining)]
135140
public void ExpireEntries<P>(ref ConcurrentLfuCore<K, V, TimeOrderNode<K, V>, P> cache) where P : struct, INodePolicy<K, V, TimeOrderNode<K, V>>
136141
{
137-
wheel.Advance(ref cache, current);
142+
wheel.Advance(ref cache, Duration.SinceEpoch());
138143
}
139144
}
140145
}

0 commit comments

Comments
 (0)