Skip to content

Commit 0b515df

Browse files
authored
fix remove (#20)
* fix remove * update git url * fix race with lock * fix build * update readme
1 parent 2d59840 commit 0b515df

File tree

10 files changed

+247
-47
lines changed

10 files changed

+247
-47
lines changed

Lightweight.Caching.Benchmarks/Lru/LruCycle.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public class LruCycle
2222
private static readonly FastConcurrentLru<int, int> fastConcurrentLru = new FastConcurrentLru<int, int>(8, 9, EqualityComparer<int>.Default);
2323
private static readonly FastConcurrentTLru<int, int> fastConcurrentTLru = new FastConcurrentTLru<int, int>(8, 9, EqualityComparer<int>.Default, TimeSpan.FromMinutes(1));
2424

25-
private static MemoryCache memoryCache = MemoryCache.Default;
25+
private static MemoryCache memoryCache = System.Runtime.Caching.MemoryCache.Default;
2626

2727
[GlobalSetup]
2828
public void GlobalSetup()
@@ -60,7 +60,7 @@ public void GlobalSetup()
6060
}
6161

6262
[Benchmark(Baseline = true, OperationsPerInvoke = 24)]
63-
public void ConcurrentDictionaryGetOrAdd()
63+
public void ConcurrentDictionary()
6464
{
6565
Func<int, int> func = x => x;
6666
dictionary.GetOrAdd(1, func);
@@ -93,7 +93,7 @@ public void ConcurrentDictionaryGetOrAdd()
9393
}
9494

9595
[Benchmark(OperationsPerInvoke = 24)]
96-
public void FastConcurrentLruGetOrAdd()
96+
public void FastConcurrentLru()
9797
{
9898
// size is 9, so segment size is 3. 6 items will cause queue cycling
9999
// without eviction. Hot => cold when not accessed.
@@ -128,7 +128,7 @@ public void FastConcurrentLruGetOrAdd()
128128
}
129129

130130
[Benchmark(OperationsPerInvoke = 24)]
131-
public void ConcurrentLruGetOrAdd()
131+
public void ConcurrentLru()
132132
{
133133
// size is 9, so segment size is 3. 6 items will cause queue cycling
134134
// without eviction. Hot => cold when not accessed.
@@ -163,7 +163,7 @@ public void ConcurrentLruGetOrAdd()
163163
}
164164

165165
[Benchmark(OperationsPerInvoke = 24)]
166-
public void FastConcurrentTLruGetOrAdd()
166+
public void FastConcurrentTLru()
167167
{
168168
// size is 9, so segment size is 3. 6 items will cause queue cycling
169169
// without eviction. Hot => cold when not accessed.
@@ -198,7 +198,7 @@ public void FastConcurrentTLruGetOrAdd()
198198
}
199199

200200
[Benchmark(OperationsPerInvoke = 24)]
201-
public void ConcurrentTLruGetOrAdd()
201+
public void ConcurrentTLru()
202202
{
203203
// size is 9, so segment size is 3. 6 items will cause queue cycling
204204
// without eviction. Hot => cold when not accessed.
@@ -233,7 +233,7 @@ public void ConcurrentTLruGetOrAdd()
233233
}
234234

235235
[Benchmark(OperationsPerInvoke = 24)]
236-
public void ClassicLruGetOrAdd()
236+
public void ClassicLru()
237237
{
238238
// size is 9, so segment size is 3. 6 items will cause queue cycling
239239
// without eviction. Hot => cold when not accessed.
@@ -268,7 +268,7 @@ public void ClassicLruGetOrAdd()
268268
}
269269

270270
[Benchmark(OperationsPerInvoke = 24)]
271-
public void MemoryCacheGetStringKey()
271+
public void MemoryCache()
272272
{
273273
memoryCache.Get("1");
274274
memoryCache.Get("2");

Lightweight.Caching.Benchmarks/Lru/LruGetOrAddTest.cs

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,6 @@ public void ConcurrentDictionaryGetOrAdd()
3838
dictionary.GetOrAdd(1, func);
3939
}
4040

41-
//[Benchmark()]
42-
//public DateTime DateTimeUtcNow()
43-
//{
44-
// return DateTime.UtcNow;
45-
//}
46-
4741
[Benchmark()]
4842
public void FastConcurrentLruGetOrAdd()
4943
{
@@ -79,12 +73,6 @@ public void ClassicLruGetOrAdd()
7973
classicLru.GetOrAdd(1, func);
8074
}
8175

82-
//[Benchmark()]
83-
//public void MemoryCacheGetIntKey()
84-
//{
85-
// memoryCache.Get(key.ToString());
86-
//}
87-
8876
[Benchmark()]
8977
public void MemoryCacheGetStringKey()
9078
{
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
using System;
2+
using System.Collections.Concurrent;
3+
using System.Collections.Generic;
4+
using System.Runtime.Caching;
5+
using System.Text;
6+
using BenchmarkDotNet.Attributes;
7+
using Lightweight.Caching.Lru;
8+
9+
namespace Lightweight.Caching.Benchmarks.Lru
10+
{
11+
[MemoryDiagnoser]
12+
public class MissHitHitRemove
13+
{
14+
const int capacity = 9;
15+
const int arraySize = 16;
16+
17+
private static readonly ConcurrentDictionary<int, byte[]> dictionary = new ConcurrentDictionary<int, byte[]>(8, capacity, EqualityComparer<int>.Default);
18+
19+
private static readonly ClassicLru<int, byte[]> classicLru = new ClassicLru<int, byte[]>(8, capacity, EqualityComparer<int>.Default);
20+
private static readonly ConcurrentLru<int, byte[]> concurrentLru = new ConcurrentLru<int, byte[]>(8, capacity, EqualityComparer<int>.Default);
21+
private static readonly ConcurrentTLru<int, byte[]> concurrentTlru = new ConcurrentTLru<int, byte[]>(8, capacity, EqualityComparer<int>.Default, TimeSpan.FromMinutes(10));
22+
private static readonly FastConcurrentLru<int, byte[]> fastConcurrentLru = new FastConcurrentLru<int, byte[]>(8, capacity, EqualityComparer<int>.Default);
23+
private static readonly FastConcurrentTLru<int, byte[]> fastConcurrentTLru = new FastConcurrentTLru<int, byte[]>(8, capacity, EqualityComparer<int>.Default, TimeSpan.FromMinutes(1));
24+
25+
private static MemoryCache memoryCache = System.Runtime.Caching.MemoryCache.Default;
26+
27+
[Benchmark(Baseline = true)]
28+
public void ConcurrentDictionary()
29+
{
30+
Func<int, byte[]> func = x => new byte[arraySize];
31+
dictionary.GetOrAdd(1, func);
32+
33+
dictionary.GetOrAdd(1, func);
34+
dictionary.GetOrAdd(1, func);
35+
36+
dictionary.TryRemove(1, out var removed);
37+
}
38+
39+
[Benchmark()]
40+
public void FastConcurrentLru()
41+
{
42+
Func<int, byte[]> func = x => new byte[arraySize];
43+
fastConcurrentLru.GetOrAdd(1, func);
44+
45+
fastConcurrentLru.GetOrAdd(1, func);
46+
fastConcurrentLru.GetOrAdd(1, func);
47+
48+
fastConcurrentLru.TryRemove(1);
49+
}
50+
51+
[Benchmark()]
52+
public void ConcurrentLru()
53+
{
54+
Func<int, byte[]> func = x => new byte[arraySize];
55+
concurrentLru.GetOrAdd(1, func);
56+
57+
concurrentLru.GetOrAdd(1, func);
58+
concurrentLru.GetOrAdd(1, func);
59+
60+
concurrentLru.TryRemove(1);
61+
}
62+
63+
[Benchmark()]
64+
public void FastConcurrentTlru()
65+
{
66+
Func<int, byte[]> func = x => new byte[arraySize];
67+
fastConcurrentTLru.GetOrAdd(1, func);
68+
69+
fastConcurrentTLru.GetOrAdd(1, func);
70+
fastConcurrentTLru.GetOrAdd(1, func);
71+
72+
fastConcurrentTLru.TryRemove(1);
73+
}
74+
75+
[Benchmark()]
76+
public void ConcurrentTlru()
77+
{
78+
Func<int, byte[]> func = x => new byte[arraySize];
79+
concurrentTlru.GetOrAdd(1, func);
80+
81+
concurrentTlru.GetOrAdd(1, func);
82+
concurrentTlru.GetOrAdd(1, func);
83+
84+
concurrentTlru.TryRemove(1);
85+
}
86+
87+
[Benchmark()]
88+
public void ClassicLru()
89+
{
90+
Func<int, byte[]> func = x => new byte[arraySize];
91+
classicLru.GetOrAdd(1, func);
92+
93+
classicLru.GetOrAdd(1, func);
94+
classicLru.GetOrAdd(1, func);
95+
96+
classicLru.TryRemove(1);
97+
}
98+
99+
[Benchmark()]
100+
public void MemoryCache()
101+
{
102+
if (memoryCache.Get("1") == null)
103+
{
104+
memoryCache.Set("1", new byte[arraySize], new CacheItemPolicy());
105+
}
106+
107+
if (memoryCache.Get("1") == null)
108+
{
109+
memoryCache.Set("1", new byte[arraySize], new CacheItemPolicy());
110+
}
111+
112+
if (memoryCache.Get("1") == null)
113+
{
114+
memoryCache.Set("1", new byte[arraySize], new CacheItemPolicy());
115+
}
116+
117+
memoryCache.Remove("1");
118+
}
119+
}
120+
}

Lightweight.Caching.Benchmarks/SegmentedLruTests.cs renamed to Lightweight.Caching.Benchmarks/PrimitiveBenchmarks.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
namespace Lightweight.Caching.Benchmarks
1111
{
1212
[MemoryDiagnoser]
13-
public class SegmentedLruTests
13+
public class PrimitiveBenchmarks
1414
{
1515
private static readonly ConcurrentDictionary<int, int> dictionary = new ConcurrentDictionary<int, int>(8, 9, EqualityComparer<int>.Default);
1616
LinkedList<int> intList = new LinkedList<int>(new int[] { 1, 2, 3 });
@@ -45,6 +45,12 @@ public void LinkedListLockSwapFirstToLast()
4545
}
4646
}
4747

48+
[Benchmark()]
49+
public DateTime DateTimeUtcNow()
50+
{
51+
return DateTime.UtcNow;
52+
}
53+
4854
[Benchmark()]
4955
public void DictionaryGetOrAdd()
5056
{

Lightweight.Caching.Benchmarks/Program.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ class Program
1515
static void Main(string[] args)
1616
{
1717
var summary = BenchmarkRunner
18-
.Run<LruCycle>(ManualConfig.Create(DefaultConfig.Instance)
18+
.Run<MissHitHitRemove>(ManualConfig.Create(DefaultConfig.Instance)
1919
.AddJob(Job.RyuJitX64));
2020
}
2121
}

Lightweight.Caching.UnitTests/Lru/ConcurrentLruTests.cs

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -141,14 +141,14 @@ public async Task WhenDifferentKeysAreRequesteValueIsCreatedForEachAsync()
141141
[Fact]
142142
public void WhenValuesAreNotReadAndMoreKeysRequestedThanCapacityCountDoesNotIncrease()
143143
{
144-
int capacity = hotCap + coldCap;
145-
for (int i = 0; i < capacity + 1; i++)
144+
int hotColdCapacity = hotCap + coldCap;
145+
for (int i = 0; i < hotColdCapacity + 1; i++)
146146
{
147147
lru.GetOrAdd(i, valueFactory.Create);
148148
}
149149

150-
lru.Count.Should().Be(capacity);
151-
valueFactory.timesCalled.Should().Be(capacity + 1);
150+
lru.Count.Should().Be(hotColdCapacity);
151+
valueFactory.timesCalled.Should().Be(hotColdCapacity + 1);
152152
}
153153

154154
[Fact]
@@ -352,7 +352,21 @@ public void WhenKeyDoesNotExistTryRemoveReturnsFalse()
352352
lru.TryRemove(2).Should().BeFalse();
353353
}
354354

355-
private class DisposableItem : IDisposable
355+
[Fact]
356+
public void WhenRepeatedlyAddingAndRemovingSameValueLruRemainsInConsistentState()
357+
{
358+
int capacity = hotCap + coldCap + warmCap;
359+
for (int i = 0; i < capacity; i++)
360+
{
361+
// Because TryRemove leaves the item in the queue, when it is eventually removed
362+
// from the cold queue, it should not remove the newly created value.
363+
lru.GetOrAdd(1, valueFactory.Create);
364+
lru.TryGet(1, out var value).Should().BeTrue();
365+
lru.TryRemove(1);
366+
}
367+
}
368+
369+
private class DisposableItem : IDisposable
356370
{
357371
public bool IsDisposed { get; private set; }
358372

Lightweight.Caching/Lightweight.Caching.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
<Version>0.9.1</Version>
1212
<Copyright>Copyright © Alex Peck 2020</Copyright>
1313
<PackageProjectUrl></PackageProjectUrl>
14-
<RepositoryUrl>https://github.com/plexcake/Lightweight.Caching</RepositoryUrl>
14+
<RepositoryUrl>https://github.com/bitfaster/Lightweight.Caching</RepositoryUrl>
1515
<PackageTags>Cache</PackageTags>
1616
</PropertyGroup>
1717

Lightweight.Caching/Lru/LruItem.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@ namespace Lightweight.Caching.Lru
99
public class LruItem<K, V>
1010
{
1111
private bool wasAccessed;
12+
private bool wasRemoved;
1213

13-
public LruItem(K k, V v)
14+
public LruItem(K k, V v)
1415
{
1516
this.Key = k;
1617
this.Value = v;
@@ -25,5 +26,11 @@ public bool WasAccessed
2526
get => this.wasAccessed;
2627
set => this.wasAccessed = value;
2728
}
28-
}
29+
30+
public bool WasRemoved
31+
{
32+
get => this.wasRemoved;
33+
set => this.wasRemoved = value;
34+
}
35+
}
2936
}

0 commit comments

Comments
 (0)