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
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
<NoWarn>1701;1702;CS8002</NoWarn>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<NoWarn>1701;1702,CS8002</NoWarn>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.12.1" />
<PackageReference Include="MathNet.Numerics" Version="4.11.0" />
Expand Down
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<ZipDistribution>(ManualConfig.Create(DefaultConfig.Instance)
.Run<LruCycle>(ManualConfig.Create(DefaultConfig.Instance)
.AddJob(Job.RyuJitX64));
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
<NoWarn>1701;1702;CS8002</NoWarn>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<NoWarn>1701;1702,CS8002</NoWarn>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="ConsoleTables" Version="2.4.1" />
<PackageReference Include="CsvHelper" Version="15.0.5" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@

namespace BitFaster.Caching.UnitTests.Lru
{
public class TLruPolicyTests
public class TLruDateTimePolicyTests
{
private readonly TLruPolicy<int, int> policy = new TLruPolicy<int, int>(TimeSpan.FromSeconds(10));
private readonly TLruDateTimePolicy<int, int> policy = new TLruDateTimePolicy<int, int>(TimeSpan.FromSeconds(10));

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

namespace BitFaster.Caching.UnitTests.Lru
{
public class TLruTicksPolicyTests
{
private readonly TLruTicksPolicy<int, int> policy = new TLruTicksPolicy<int, int>(TimeSpan.FromSeconds(10));

[Fact]
public void CreateItemInitializesKeyAndValue()
{
var item = this.policy.CreateItem(1, 2);

item.Key.Should().Be(1);
item.Value.Should().Be(2);
}

[Fact]
public void CreateItemInitializesTimestampToNow()
{
var item = this.policy.CreateItem(1, 2);

item.TickCount.Should().BeCloseTo(Environment.TickCount, 20);
}

[Fact]
public void TouchUpdatesItemWasAccessed()
{
var item = this.policy.CreateItem(1, 2);
item.WasAccessed = false;

this.policy.Touch(item);

item.WasAccessed.Should().BeTrue();
}

[Fact]
public void WhenItemIsExpiredShouldDiscardIsTrue()
{
var item = this.policy.CreateItem(1, 2);
item.TickCount = Environment.TickCount - (int)TimeSpan.FromSeconds(11).ToEnvTicks();

this.policy.ShouldDiscard(item).Should().BeTrue();
}

[Fact]
public void WhenItemIsNotExpiredShouldDiscardIsFalse()
{
var item = this.policy.CreateItem(1, 2);
item.TickCount = Environment.TickCount - (int)TimeSpan.FromSeconds(9).ToEnvTicks();

this.policy.ShouldDiscard(item).Should().BeFalse();
}

[Theory]
[InlineData(false, true, ItemDestination.Remove)]
[InlineData(true, true, ItemDestination.Remove)]
[InlineData(true, false, ItemDestination.Warm)]
[InlineData(false, false, ItemDestination.Cold)]
public void RouteHot(bool wasAccessed, bool isExpired, ItemDestination expectedDestination)
{
var item = CreateItem(wasAccessed, isExpired);

this.policy.RouteHot(item).Should().Be(expectedDestination);
}

[Theory]
[InlineData(false, true, ItemDestination.Remove)]
[InlineData(true, true, ItemDestination.Remove)]
[InlineData(true, false, ItemDestination.Warm)]
[InlineData(false, false, ItemDestination.Cold)]
public void RouteWarm(bool wasAccessed, bool isExpired, ItemDestination expectedDestination)
{
var item = CreateItem(wasAccessed, isExpired);

this.policy.RouteWarm(item).Should().Be(expectedDestination);
}

[Theory]
[InlineData(false, true, ItemDestination.Remove)]
[InlineData(true, true, ItemDestination.Remove)]
[InlineData(true, false, ItemDestination.Warm)]
[InlineData(false, false, ItemDestination.Remove)]
public void RouteCold(bool wasAccessed, bool isExpired, ItemDestination expectedDestination)
{
var item = CreateItem(wasAccessed, isExpired);

this.policy.RouteCold(item).Should().Be(expectedDestination);
}

private TickCountLruItem<int, int> CreateItem(bool wasAccessed, bool isExpired)
{
var item = this.policy.CreateItem(1, 2);

item.WasAccessed = wasAccessed;

if (isExpired)
{
item.TickCount = Environment.TickCount - TimeSpan.FromSeconds(11).ToEnvTicks();
}

return item;
}
}

public static class TimeSpanExtensions
{
public static int ToEnvTicks(this TimeSpan ts)
{
return (int)ts.TotalMilliseconds;
}
}
}
4 changes: 2 additions & 2 deletions BitFaster.Caching/Lru/ConcurrentTLru.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@

namespace BitFaster.Caching.Lru
{
public sealed class ConcurrentTLru<K, V> : TemplateConcurrentLru<K, V, TimeStampedLruItem<K, V>, TLruPolicy<K, V>, HitCounter>
public sealed class ConcurrentTLru<K, V> : TemplateConcurrentLru<K, V, TimeStampedLruItem<K, V>, TLruDateTimePolicy<K, V>, HitCounter>
{
public ConcurrentTLru(int concurrencyLevel, int capacity, IEqualityComparer<K> comparer, TimeSpan timeToLive)
: base(concurrencyLevel, capacity, comparer, new TLruPolicy<K, V>(timeToLive), new HitCounter())
: base(concurrencyLevel, capacity, comparer, new TLruDateTimePolicy<K, V>(timeToLive), new HitCounter())
{
}

Expand Down
4 changes: 2 additions & 2 deletions BitFaster.Caching/Lru/FastConcurrentTLru.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@

namespace BitFaster.Caching.Lru
{
public sealed class FastConcurrentTLru<K, V> : TemplateConcurrentLru<K, V, TimeStampedLruItem<K, V>, TLruPolicy<K, V>, NullHitCounter>
public sealed class FastConcurrentTLru<K, V> : TemplateConcurrentLru<K, V, TickCountLruItem<K, V>, TLruTicksPolicy<K, V>, NullHitCounter>
{
public FastConcurrentTLru(int concurrencyLevel, int capacity, IEqualityComparer<K> comparer, TimeSpan timeToLive)
: base(concurrencyLevel, capacity, comparer, new TLruPolicy<K, V>(timeToLive), new NullHitCounter())
: base(concurrencyLevel, capacity, comparer, new TLruTicksPolicy<K, V>(timeToLive), new NullHitCounter())
{
}
}
Expand Down
19 changes: 19 additions & 0 deletions BitFaster.Caching/Lru/TickCountLruItem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace BitFaster.Caching.Lru
{
public class TickCountLruItem<K, V> : LruItem<K, V>
{
public TickCountLruItem(K key, V value)
: base(key, value)
{
this.TickCount = Environment.TickCount;
}

public int TickCount { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ namespace BitFaster.Caching.Lru
/// Time aware Least Recently Used (TLRU) is a variant of LRU which discards the least
/// recently used items first, and any item that has expired.
/// </summary>
public readonly struct TLruPolicy<K, V> : IPolicy<K, V, TimeStampedLruItem<K, V>>
public readonly struct TLruDateTimePolicy<K, V> : IPolicy<K, V, TimeStampedLruItem<K, V>>
{
private readonly TimeSpan timeToLive;

public TLruPolicy(TimeSpan timeToLive)
public TLruDateTimePolicy(TimeSpan timeToLive)
{
this.timeToLive = timeToLive;
}
Expand Down
99 changes: 99 additions & 0 deletions BitFaster.Caching/Lru/TlruTicksPolicy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;

namespace BitFaster.Caching.Lru
{
/// <summary>
/// Time aware Least Recently Used (TLRU) is a variant of LRU which discards the least
/// recently used items first, and any item that has expired.
/// </summary>
/// <remarks>
/// This class measures time using Environment.TickCount, which is significantly faster
/// than DateTime.Now. However, if the process runs for longer than 24.8 days, the integer
/// value will wrap and time measurement will become invalid.
/// </remarks>
public readonly struct TLruTicksPolicy<K, V> : IPolicy<K, V, TickCountLruItem<K, V>>
{
private readonly int timeToLive;

public TLruTicksPolicy(TimeSpan timeToLive)
{
this.timeToLive = (int)timeToLive.TotalMilliseconds;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public TickCountLruItem<K, V> CreateItem(K key, V value)
{
return new TickCountLruItem<K, V>(key, value);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Touch(TickCountLruItem<K, V> item)
{
item.WasAccessed = true;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool ShouldDiscard(TickCountLruItem<K, V> item)
{
if (Environment.TickCount - item.TickCount > this.timeToLive)
{
return true;
}

return false;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ItemDestination RouteHot(TickCountLruItem<K, V> item)
{
if (this.ShouldDiscard(item))
{
return ItemDestination.Remove;
}

if (item.WasAccessed)
{
return ItemDestination.Warm;
}

return ItemDestination.Cold;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ItemDestination RouteWarm(TickCountLruItem<K, V> item)
{
if (this.ShouldDiscard(item))
{
return ItemDestination.Remove;
}

if (item.WasAccessed)
{
return ItemDestination.Warm;
}

return ItemDestination.Cold;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ItemDestination RouteCold(TickCountLruItem<K, V> item)
{
if (this.ShouldDiscard(item))
{
return ItemDestination.Remove;
}

if (item.WasAccessed)
{
return ItemDestination.Warm;
}

return ItemDestination.Remove;
}
}
}