Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
133 changes: 133 additions & 0 deletions BitFaster.Caching.Benchmarks/Lru/LruCycle2.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Columns;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Jobs;
using BitFaster.Caching.Lru;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Runtime.Caching;
using System.Text;

namespace BitFaster.Caching.Benchmarks.Lru
{
[MemoryDiagnoser]
public class LruCycle2
{
const int capacity = 9;
const int iters = 10;

private static readonly ConcurrentDictionary<int, int> dictionary = new ConcurrentDictionary<int, int>(8, 9, EqualityComparer<int>.Default);

private static readonly ClassicLru<int, int> classicLru = new ClassicLru<int, int>(8, capacity, EqualityComparer<int>.Default);
private static readonly ConcurrentLru<int, int> concurrentLru = new ConcurrentLru<int, int>(8, capacity, EqualityComparer<int>.Default);
private static readonly ConcurrentTLru<int, int> concurrentTLru = new ConcurrentTLru<int, int>(8, capacity, EqualityComparer<int>.Default, TimeSpan.FromMinutes(1));
private static readonly FastConcurrentLru<int, int> fastConcurrentLru = new FastConcurrentLru<int, int>(8, capacity, EqualityComparer<int>.Default);
private static readonly FastConcurrentTLru<int, int> fastConcurrentTLru = new FastConcurrentTLru<int, int>(8, capacity, EqualityComparer<int>.Default, TimeSpan.FromMinutes(1));

private static MemoryCache memoryCache = System.Runtime.Caching.MemoryCache.Default;

[Benchmark(Baseline = true, OperationsPerInvoke = 24)]
public void ConcurrentDictionary()
{
Func<int, int> func = x => x;

for (int j = 0; j < iters; j++)
for (int i = 0; i < capacity + 1; i++)
{
dictionary.GetOrAdd(i, func);

// simulate what the LRU does
if (i == capacity)
{
dictionary.TryRemove(1, out var removed);
}
}
}

[Benchmark(OperationsPerInvoke = 24)]
public void FastConcurrentLru()
{
Func<int, int> func = x => x;

for (int j = 0; j < iters; j++)
for (int i = 0; i < capacity + 1; i++)
{
fastConcurrentLru.GetOrAdd(i, func);
}
}

[Benchmark(OperationsPerInvoke = 24)]
public void ConcurrentLru()
{
Func<int, int> func = x => x;

for (int j = 0; j < iters; j++)
for (int i = 0; i < capacity + 1; i++)
{
concurrentLru.GetOrAdd(i, func);
}
}

[Benchmark(OperationsPerInvoke = 24)]
public void FastConcurrentTLru()
{
Func<int, int> func = x => x;

for (int j = 0; j < iters; j++)
for (int i = 0; i < capacity + 1; i++)
{
fastConcurrentTLru.GetOrAdd(i, func);
}
}

[Benchmark(OperationsPerInvoke = 24)]
public void ConcurrentTLru()
{
Func<int, int> func = x => x;

for (int j = 0; j < iters; j++)
for (int i = 0; i < capacity + 1; i++)
{
concurrentTLru.GetOrAdd(i, func);
}
}

[Benchmark(OperationsPerInvoke = 24)]
public void ClassicLru()
{
Func<int, int> func = x => x;

for (int j = 0; j < iters; j++)
for (int i = 0; i < capacity + 1; i++)
{
classicLru.GetOrAdd(i, func);
}
}

[Benchmark(OperationsPerInvoke = 24)]
public void MemoryCache()
{

Func<int, int> func = x => x;

for (int j = 0; j < iters; j++)
for (int i = 0; i < capacity + 1; i++)
{
string key = i.ToString();
var v = memoryCache.Get(key);

if (v == null)
{
memoryCache.Set(key, "test", new CacheItemPolicy());
}

// simulate what the LRU does
if (i == capacity)
{
memoryCache.Remove("1");
}
}
}
}
}
2 changes: 1 addition & 1 deletion BitFaster.Caching.Benchmarks/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class Program
static void Main(string[] args)
{
var summary = BenchmarkRunner
.Run<MissHitHitRemove>(ManualConfig.Create(DefaultConfig.Instance)
.Run<LruCycle2>(ManualConfig.Create(DefaultConfig.Instance)
.AddJob(Job.RyuJitX64));
}
}
Expand Down
72 changes: 69 additions & 3 deletions BitFaster.Caching.UnitTests/Lru/ClassicLruTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,31 @@ public class ClassicLruTests
private ClassicLru<int, string> lru = new ClassicLru<int, string>(1, capacity, EqualityComparer<int>.Default);
ValueFactory valueFactory = new ValueFactory();

[Fact]
[Fact]
public void WhenConcurrencyIsLessThan1CtorThrows()
{
Action constructor = () => { var x = new ClassicLru<int, string>(0, 3, EqualityComparer<int>.Default); };

constructor.Should().Throw<ArgumentOutOfRangeException>();
}

[Fact]
public void WhenCapacityIsLessThan3CtorThrows()
{
Action constructor = () => { var x = new ClassicLru<int, string>(1, 2, EqualityComparer<int>.Default); };

constructor.Should().Throw<ArgumentOutOfRangeException>();
}

[Fact]
public void WhenComparerIsNullCtorThrows()
{
Action constructor = () => { var x = new ClassicLru<int, string>(1, 3, null); };

constructor.Should().Throw<ArgumentNullException>();
}

[Fact]
public void WhenItemIsAddedCountIsCorrect()
{
lru.Count.Should().Be(0);
Expand Down Expand Up @@ -149,7 +173,37 @@ public void WhenMoreKeysRequestedThanCapacityOldestItemIsEvicted()
valueFactory.timesCalled.Should().Be(capacity + 2);
}

[Fact]
[Fact]
public void WhenValueExpiresItIsDisposed()
{
var lruOfDisposable = new ClassicLru<int, DisposableItem>(1, 6, EqualityComparer<int>.Default);
var disposableValueFactory = new DisposableValueFactory();

for (int i = 0; i < 7; i++)
{
lruOfDisposable.GetOrAdd(i, disposableValueFactory.Create);
}

disposableValueFactory.Items[0].IsDisposed.Should().BeTrue();
disposableValueFactory.Items[1].IsDisposed.Should().BeFalse();
}

[Fact]
public async Task WhenValueExpiresAsyncItIsDisposed()
{
var lruOfDisposable = new ClassicLru<int, DisposableItem>(1, 6, EqualityComparer<int>.Default);
var disposableValueFactory = new DisposableValueFactory();

for (int i = 0; i < 7; i++)
{
await lruOfDisposable.GetOrAddAsync(i, disposableValueFactory.CreateAsync);
}

disposableValueFactory.Items[0].IsDisposed.Should().BeTrue();
disposableValueFactory.Items[1].IsDisposed.Should().BeFalse();
}

[Fact]
public void WhenKeyDoesNotExistTryGetReturnsFalse()
{
lru.GetOrAdd(1, valueFactory.Create);
Expand All @@ -176,7 +230,19 @@ public void WhenKeyExistsTryRemoveRemovesItemAndReturnsTrue()
lru.TryGet(1, out var value).Should().BeFalse();
}

[Fact]
[Fact]
public void WhenItemIsRemovedItIsDisposed()
{
var lruOfDisposable = new ClassicLru<int, DisposableItem>(1, 6, EqualityComparer<int>.Default);
var disposableValueFactory = new DisposableValueFactory();

lruOfDisposable.GetOrAdd(1, disposableValueFactory.Create);
lruOfDisposable.TryRemove(1);

disposableValueFactory.Items[1].IsDisposed.Should().BeTrue();
}

[Fact]
public void WhenKeyDoesNotExistTryRemoveReturnsFalse()
{
lru.GetOrAdd(1, valueFactory.Create);
Expand Down
24 changes: 0 additions & 24 deletions BitFaster.Caching.UnitTests/Lru/ConcurrentLruTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -365,29 +365,5 @@ public void WhenRepeatedlyAddingAndRemovingSameValueLruRemainsInConsistentState(
lru.TryRemove(1);
}
}

private class DisposableItem : IDisposable
{
public bool IsDisposed { get; private set; }

public void Dispose()
{
this.IsDisposed = true;
}
}

private class DisposableValueFactory
{
private Dictionary<int, DisposableItem> items = new Dictionary<int, DisposableItem>();

public Dictionary<int, DisposableItem> Items => this.items;

public DisposableItem Create(int key)
{
var item = new DisposableItem();
items.Add(key, item);
return item;
}
}
}
}
16 changes: 16 additions & 0 deletions BitFaster.Caching.UnitTests/Lru/DisposableItem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace BitFaster.Caching.UnitTests.Lru
{
public class DisposableItem : IDisposable
{
public bool IsDisposed { get; private set; }

public void Dispose()
{
this.IsDisposed = true;
}
}
}
28 changes: 28 additions & 0 deletions BitFaster.Caching.UnitTests/Lru/DisposableValueFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;

namespace BitFaster.Caching.UnitTests.Lru
{
public class DisposableValueFactory
{
private Dictionary<int, DisposableItem> items = new Dictionary<int, DisposableItem>();

public Dictionary<int, DisposableItem> Items => this.items;

public DisposableItem Create(int key)
{
var item = new DisposableItem();
items.Add(key, item);
return item;
}

public Task<DisposableItem> CreateAsync(int key)
{
var item = new DisposableItem();
items.Add(key, item);
return Task.FromResult(item);
}
}
}
16 changes: 16 additions & 0 deletions BitFaster.Caching.UnitTests/ReferenceCountTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,5 +52,21 @@ public void WhenObjectsAreDifferentHashcodesAreDifferent()

a.GetHashCode().Should().NotBe(b.GetHashCode());
}

[Fact]
public void WhenObjectDisposed()
{
var a = new ReferenceCount<Disposable>(new Disposable());
var b = a.DecrementCopy();

b.Invoking(rc => rc.IncrementCopy()).Should().Throw<ObjectDisposedException>();
}

private class Disposable : IDisposable
{
public void Dispose()
{
}
}
}
}
Loading