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
2 changes: 1 addition & 1 deletion BitFaster.Caching.Benchmarks/DisposerBench.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ namespace BitFaster.Caching.Benchmarks
// https://github.com/dotnet/runtime/issues/4920
[SimpleJob(RuntimeMoniker.Net48)]
[SimpleJob(RuntimeMoniker.Net60)]
[DisassemblyDiagnoser(printSource: true, maxDepth:3)]
[DisassemblyDiagnoser(printSource: true, maxDepth: 3)]
[MemoryDiagnoser]
public class DisposerBench
{
Expand Down
42 changes: 34 additions & 8 deletions BitFaster.Caching.Benchmarks/Lru/LruCycleBench.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using BenchmarkDotNet.Attributes;
Expand All @@ -16,13 +17,14 @@ namespace BitFaster.Caching.Benchmarks.Lru
// DefaultJob : .NET 6.0.0 (6.0.21.52210), X64 RyuJIT


//| Method | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Code Size | Allocated |
//|------------------- |---------:|---------:|---------:|------:|--------:|-------:|----------:|----------:|
//| FastConcurrentLru | 23.25 us | 0.128 us | 0.114 us | 1.00 | 0.00 | 2.1362 | 5 KB | 9 KB |
//| ConcurrentLru | 23.78 us | 0.116 us | 0.097 us | 1.02 | 0.01 | 2.1362 | 5 KB | 9 KB |
//| FastConcurrentTLru | 32.17 us | 0.463 us | 0.433 us | 1.38 | 0.02 | 2.3193 | 6 KB | 10 KB |
//| ConcurrentTLru | 32.52 us | 0.386 us | 0.361 us | 1.40 | 0.02 | 2.3193 | 6 KB | 10 KB |
//| ClassicLru | 16.29 us | 0.195 us | 0.163 us | 0.70 | 0.01 | 3.2959 | 5 KB | 14 KB |
//| Method | Mean | Error | StdDev | Ratio | Code Size | Gen 0 | Allocated |
//|------------------- |---------:|---------:|---------:|------:|----------:|-------:|----------:|
//| FastConcurrentLru | 22.86 us | 0.183 us | 0.162 us | 1.00 | 5 KB | 2.1362 | 9 KB |
//| ConcurrentLru | 23.40 us | 0.092 us | 0.077 us | 1.02 | 5 KB | 2.1362 | 9 KB |
//| ConcurrentLruEvent | 24.23 us | 0.097 us | 0.086 us | 1.06 | 5 KB | 3.0823 | 13 KB |
//| FastConcurrentTLru | 31.70 us | 0.087 us | 0.077 us | 1.39 | 6 KB | 2.3193 | 10 KB |
//| ConcurrentTLru | 31.85 us | 0.080 us | 0.071 us | 1.39 | 6 KB | 2.3193 | 10 KB |
//| ClassicLru | 16.35 us | 0.091 us | 0.076 us | 0.72 | 4 KB | 3.2959 | 14 KB |
[SimpleJob(RuntimeMoniker.Net48)]
[SimpleJob(RuntimeMoniker.Net60)]
[DisassemblyDiagnoser(printSource: true, maxDepth: 5)]
Expand All @@ -31,11 +33,26 @@ public class LruCycleBench
{
private static readonly ClassicLru<int, int> classicLru = new ClassicLru<int, int>(8, 9, EqualityComparer<int>.Default);
private static readonly ConcurrentLru<int, int> concurrentLru = new ConcurrentLru<int, int>(8, 9, EqualityComparer<int>.Default);
private static readonly ConcurrentLru<int, int> concurrentLruEvent = new ConcurrentLru<int, int>(8, 9, EqualityComparer<int>.Default);
private static readonly ConcurrentTLru<int, int> concurrentTlru = new ConcurrentTLru<int, int>(8, 9, EqualityComparer<int>.Default, TimeSpan.FromMinutes(10));
private static readonly FastConcurrentLru<int, int> fastConcurrentLru = new FastConcurrentLru<int, int>(8, 9, EqualityComparer<int>.Default);
private static readonly FastConcurrentTLru<int, int> fastConcurrentTLru = new FastConcurrentTLru<int, int>(8, 9, EqualityComparer<int>.Default, TimeSpan.FromMinutes(1));

[Benchmark(Baseline = true)]
[GlobalSetup]
public void GlobalSetup()
{
concurrentLruEvent.ItemRemoved += OnItemRemoved;
}

public static int field;

[MethodImpl(MethodImplOptions.NoOptimization)]
private void OnItemRemoved(object sender, ItemRemovedEventArgs<int, int> e)
{
field = e.Key;
}

[Benchmark(Baseline =true)]
public void FastConcurrentLru()
{
Func<int, int> func = x => x;
Expand All @@ -53,6 +70,15 @@ public void ConcurrentLru()
concurrentLru.GetOrAdd(i, func);
}

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

for (int i = 0; i < 128; i++)
concurrentLruEvent.GetOrAdd(i, func);
}

[Benchmark()]
public void FastConcurrentTLru()
{
Expand Down
18 changes: 9 additions & 9 deletions BitFaster.Caching.Benchmarks/Lru/TLruTimeBenchmark.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,17 @@ namespace BitFaster.Caching.Benchmarks.Lru
[SimpleJob(RuntimeMoniker.Net60)]
public class TLruTimeBenchmark
{
private static readonly TemplateConcurrentLru<int, int, TimeStampedLruItem<int, int>, TLruDateTimePolicy<int, int>, NullHitCounter> dateTimeTLru
= new TemplateConcurrentLru<int, int, TimeStampedLruItem<int, int>, TLruDateTimePolicy<int, int>, NullHitCounter>
(1, 3, EqualityComparer<int>.Default, new TLruDateTimePolicy<int, int>(TimeSpan.FromSeconds(1)), new NullHitCounter());
private static readonly TemplateConcurrentLru<int, int, TimeStampedLruItem<int, int>, TLruDateTimePolicy<int, int>, NoTelemetryPolicy<int, int>> dateTimeTLru
= new TemplateConcurrentLru<int, int, TimeStampedLruItem<int, int>, TLruDateTimePolicy<int, int>, NoTelemetryPolicy<int, int>>
(1, 3, EqualityComparer<int>.Default, new TLruDateTimePolicy<int, int>(TimeSpan.FromSeconds(1)), default);

private static readonly TemplateConcurrentLru<int, int, TickCountLruItem<int, int>, TLruTicksPolicy<int, int>, NullHitCounter> tickCountTLru
= new TemplateConcurrentLru<int, int, TickCountLruItem<int, int>, TLruTicksPolicy<int, int>, NullHitCounter>
(1, 3, EqualityComparer<int>.Default, new TLruTicksPolicy<int, int>(TimeSpan.FromSeconds(1)), new NullHitCounter());
private static readonly TemplateConcurrentLru<int, int, TickCountLruItem<int, int>, TLruTicksPolicy<int, int>, NoTelemetryPolicy<int, int>> tickCountTLru
= new TemplateConcurrentLru<int, int, TickCountLruItem<int, int>, TLruTicksPolicy<int, int>, NoTelemetryPolicy<int, int>>
(1, 3, EqualityComparer<int>.Default, new TLruTicksPolicy<int, int>(TimeSpan.FromSeconds(1)), default);

private static readonly TemplateConcurrentLru<int, int, LongTickCountLruItem<int, int>, TLruLongTicksPolicy<int, int>, NullHitCounter> stopwatchTLru
= new TemplateConcurrentLru<int, int, LongTickCountLruItem<int, int>, TLruLongTicksPolicy<int, int>, NullHitCounter>
(1, 3, EqualityComparer<int>.Default, new TLruLongTicksPolicy<int, int>(TimeSpan.FromSeconds(1)), new NullHitCounter());
private static readonly TemplateConcurrentLru<int, int, LongTickCountLruItem<int, int>, TLruLongTicksPolicy<int, int>, NoTelemetryPolicy<int, int>> stopwatchTLru
= new TemplateConcurrentLru<int, int, LongTickCountLruItem<int, int>, TLruLongTicksPolicy<int, int>, NoTelemetryPolicy<int, int>>
(1, 3, EqualityComparer<int>.Default, new TLruLongTicksPolicy<int, int>(TimeSpan.FromSeconds(1)), default);

[Benchmark(Baseline = true)]
public void DateTimeUtcNow()
Expand Down
61 changes: 61 additions & 0 deletions BitFaster.Caching.UnitTests/Lru/ConcurrentLruTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@ public class ConcurrentLruTests
private ConcurrentLru<int, string> lru = new ConcurrentLru<int, string>(1, hotCap + warmCap + coldCap, EqualityComparer<int>.Default);
private ValueFactory valueFactory = new ValueFactory();

private List<ItemRemovedEventArgs<int, int>> removedItems = new List<ItemRemovedEventArgs<int, int>>();

private void OnLruItemRemoved(object sender, ItemRemovedEventArgs<int, int> e)
{
removedItems.Add(e);
}

public ConcurrentLruTests(ITestOutputHelper testOutputHelper)
{
this.testOutputHelper = testOutputHelper;
Expand Down Expand Up @@ -359,6 +366,44 @@ public void WhenValueExpiresItIsDisposed()
disposableValueFactory.Items[1].IsDisposed.Should().BeFalse();
}

[Fact]
public void WhenValueEvictedItemRemovedEventIsFired()
{
var lruEvents = new ConcurrentLru<int, int>(1, 6, EqualityComparer<int>.Default);
lruEvents.ItemRemoved += OnLruItemRemoved;

for (int i = 0; i < 6; i++)
{
lruEvents.GetOrAdd(i+1, i => i + 1);
}

removedItems.Count.Should().Be(2);

removedItems[0].Key.Should().Be(1);
removedItems[0].Value.Should().Be(2);
removedItems[0].Reason.Should().Be(ItemRemovedReason.Evicted);

removedItems[1].Key.Should().Be(2);
removedItems[1].Value.Should().Be(3);
removedItems[1].Reason.Should().Be(ItemRemovedReason.Evicted);
}

[Fact]
public void WhenItemRemovedEventIsUnregisteredEventIsNotFired()
{
var lruEvents = new ConcurrentLru<int, int>(1, 6, EqualityComparer<int>.Default);

lruEvents.ItemRemoved += OnLruItemRemoved;
lruEvents.ItemRemoved -= OnLruItemRemoved;

for (int i = 0; i < 6; i++)
{
lruEvents.GetOrAdd(i + 1, i => i + 1);
}

removedItems.Count.Should().Be(0);
}

[Fact]
public void WhenKeyExistsTryRemoveRemovesItemAndReturnsTrue()
{
Expand All @@ -380,6 +425,22 @@ public void WhenItemIsRemovedItIsDisposed()
disposableValueFactory.Items[1].IsDisposed.Should().BeTrue();
}

[Fact]
public void WhenItemIsRemovedRemovedEventIsFired()
{
var lruEvents = new ConcurrentLru<int, int>(1, 6, EqualityComparer<int>.Default);
lruEvents.ItemRemoved += OnLruItemRemoved;

lruEvents.GetOrAdd(1, i => i+2);

lruEvents.TryRemove(1).Should().BeTrue();

removedItems.Count().Should().Be(1);
removedItems[0].Key.Should().Be(1);
removedItems[0].Value.Should().Be(3);
removedItems[0].Reason.Should().Be(ItemRemovedReason.Removed);
}

[Fact]
public void WhenKeyDoesNotExistTryRemoveReturnsFalse()
{
Expand Down
45 changes: 45 additions & 0 deletions BitFaster.Caching.UnitTests/Lru/ConcurrentTLruTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ public class ConcurrentTLruTests

private ValueFactory valueFactory = new ValueFactory();

private List<ItemRemovedEventArgs<int, int>> removedItems = new List<ItemRemovedEventArgs<int, int>>();

private void OnLruItemRemoved(object sender, ItemRemovedEventArgs<int, int> e)
{
removedItems.Add(e);
}

public ConcurrentTLruTests()
{
lru = new ConcurrentTLru<int, string>(1, capacity, EqualityComparer<int>.Default, timeToLive);
Expand Down Expand Up @@ -47,6 +54,44 @@ public async Task WhenItemIsExpiredItIsRemoved()
lru.TryGet(1, out var value).Should().BeFalse();
}

[Fact]
public void WhenValueEvictedItemRemovedEventIsFired()
{
var lruEvents = new ConcurrentTLru<int, int>(1, 6, EqualityComparer<int>.Default, timeToLive);
lruEvents.ItemRemoved += OnLruItemRemoved;

for (int i = 0; i < 6; i++)
{
lruEvents.GetOrAdd(i + 1, i => i + 1);
}

removedItems.Count.Should().Be(2);

removedItems[0].Key.Should().Be(1);
removedItems[0].Value.Should().Be(2);
removedItems[0].Reason.Should().Be(ItemRemovedReason.Evicted);

removedItems[1].Key.Should().Be(2);
removedItems[1].Value.Should().Be(3);
removedItems[1].Reason.Should().Be(ItemRemovedReason.Evicted);
}

[Fact]
public void WhenItemRemovedEventIsUnregisteredEventIsNotFired()
{
var lruEvents = new ConcurrentTLru<int, int>(1, 6, EqualityComparer<int>.Default, timeToLive);

lruEvents.ItemRemoved += OnLruItemRemoved;
lruEvents.ItemRemoved -= OnLruItemRemoved;

for (int i = 0; i < 6; i++)
{
lruEvents.GetOrAdd(i + 1, i => i + 1);
}

removedItems.Count.Should().Be(0);
}

[Fact]
public void WhenItemIsAddedThenRetrievedHitRatioIsHalf()
{
Expand Down
41 changes: 0 additions & 41 deletions BitFaster.Caching.UnitTests/Lru/HitCounterTests.cs

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@

namespace BitFaster.Caching.UnitTests.Lru
{
public class NullHitCounterTests
public class NoTelemetryPolicyTests
{
private NullHitCounter counter = new NullHitCounter();
private NoTelemetryPolicy<int, int> counter = new NoTelemetryPolicy<int, int>();

[Fact]
public void HitRatioIsZero()
Expand Down
66 changes: 66 additions & 0 deletions BitFaster.Caching.UnitTests/Lru/TelemetryPolicyTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
using FluentAssertions;
using BitFaster.Caching.Lru;
using System;
using System.Collections.Generic;
using System.Text;
using Xunit;

namespace BitFaster.Caching.UnitTests.Lru
{
public class TelemetryPolicyTests
{
private TelemetryPolicy<int, int> telemetryPolicy = default;

[Fact]
public void WhenHitCountAndTotalCountAreEqualRatioIs1()
{
telemetryPolicy.IncrementHit();

telemetryPolicy.HitRatio.Should().Be(1.0);
}

[Fact]
public void WhenHitCountIsEqualToMissCountRatioIsHalf()
{
telemetryPolicy.IncrementMiss();
telemetryPolicy.IncrementHit();

telemetryPolicy.HitRatio.Should().Be(0.5);
}

[Fact]
public void WhenTotalCountIsZeroRatioReturnsZero()
{
telemetryPolicy.HitRatio.Should().Be(0.0);
}

[Fact]
public void WhenOnItemRemovedInvokedEventIsFired()
{
List<ItemRemovedEventArgs<int, int>> eventList = new();

telemetryPolicy.ItemRemoved += (source, args) => eventList.Add(args);

telemetryPolicy.OnItemRemoved(1, 2, ItemRemovedReason.Evicted);

eventList.Should().HaveCount(1);
eventList[0].Key.Should().Be(1);
eventList[0].Value.Should().Be(2);
eventList[0].Reason.Should().Be(ItemRemovedReason.Evicted);
}

[Fact]
public void WhenEventSourceIsSetItemRemovedEventUsesSource()
{
List<object> eventSourceList = new();

telemetryPolicy.ItemRemoved += (source, args) => eventSourceList.Add(source);

telemetryPolicy.SetEventSource(this);
telemetryPolicy.OnItemRemoved(1, 2, ItemRemovedReason.Evicted);

eventSourceList.Should().HaveCount(1);
eventSourceList[0].Should().Be(this);
}
}
}
2 changes: 1 addition & 1 deletion BitFaster.Caching/Disposer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
namespace BitFaster.Caching
{
/// <summary>
/// A generic wrapper for object disposal. Enables JIT to inline/remove object disposal if statement reducing code size.
/// A generic wrapper for object disposal.
/// </summary>
/// <typeparam name="T">The type of object to dispose</typeparam>
public static class Disposer<T>
Expand Down
2 changes: 1 addition & 1 deletion BitFaster.Caching/Lifetime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace BitFaster.Caching
/// lifetime is disposed.
/// </summary>
/// <typeparam name="T">The type of value</typeparam>
public class Lifetime<T> : IDisposable
public sealed class Lifetime<T> : IDisposable
{
private readonly Action onDisposeAction;
private readonly ReferenceCount<T> refCount;
Expand Down
Loading