-
-
Notifications
You must be signed in to change notification settings - Fork 192
Expand file tree
/
Copy pathExpireAfter_Cache_ForStream.cs
More file actions
131 lines (108 loc) · 4.33 KB
/
Copy pathExpireAfter_Cache_ForStream.cs
File metadata and controls
131 lines (108 loc) · 4.33 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
using Bogus;
namespace DynamicData.Benchmarks.Cache;
[MemoryDiagnoser]
[MarkdownExporterAttribute.GitHub]
public class ExpireAfter_Cache_ForStream
{
public ExpireAfter_Cache_ForStream()
{
// Not exercising Moved, since ChangeAwareCache<> doesn't support it, and I'm too lazy to implement it by hand.
var changeReasons = new[]
{
ChangeReason.Add,
ChangeReason.Refresh,
ChangeReason.Remove,
ChangeReason.Update
};
// Weights are chosen to make the cache size likely to grow over time,
// exerting more pressure on the system the longer the benchmark runs.
// Also, to prevent bogus operations (E.G. you can't remove an item from an empty cache).
var changeReasonWeightsWhenCountIs0 = new[]
{
1f, // Add
0f, // Refresh
0f, // Remove
0f // Update
};
var changeReasonWeightsOtherwise = new[]
{
0.30f, // Add
0.25f, // Refresh
0.20f, // Remove
0.25f // Update
};
var maxChangeCount = 20;
var minItemLifetime = TimeSpan.FromMilliseconds(1);
var maxItemLifetime = TimeSpan.FromMilliseconds(10);
var randomizer = new Randomizer(1234567);
var nextItemId = 1;
var changeSets = ImmutableArray.CreateBuilder<IChangeSet<Item, int>>(initialCapacity: 5_000);
var cache = new ChangeAwareCache<Item, int>();
while (changeSets.Count < changeSets.Capacity)
{
var changeCount = randomizer.Int(1, maxChangeCount);
for (var i = 0; i < changeCount; ++i)
{
var changeReason = randomizer.WeightedRandom(changeReasons, cache.Count switch
{
0 => changeReasonWeightsWhenCountIs0,
_ => changeReasonWeightsOtherwise
});
switch (changeReason)
{
case ChangeReason.Add:
cache.AddOrUpdate(
item: new Item()
{
Id = nextItemId,
Lifetime = randomizer.Bool()
? TimeSpan.FromTicks(randomizer.Long(minItemLifetime.Ticks, maxItemLifetime.Ticks))
: null
},
key: nextItemId);
++nextItemId;
break;
case ChangeReason.Refresh:
cache.Refresh(cache.Keys.ElementAt(randomizer.Int(0, cache.Count - 1)));
break;
case ChangeReason.Remove:
cache.Remove(cache.Keys.ElementAt(randomizer.Int(0, cache.Count - 1)));
break;
case ChangeReason.Update:
var id = cache.Keys.ElementAt(randomizer.Int(0, cache.Count - 1));
cache.AddOrUpdate(
item: new Item()
{
Id = id,
Lifetime = randomizer.Bool()
? TimeSpan.FromTicks(randomizer.Long(minItemLifetime.Ticks, maxItemLifetime.Ticks))
: null
},
key: id);
break;
}
}
changeSets.Add(cache.CaptureChanges());
}
_changeSets = changeSets.MoveToImmutable();
}
[Benchmark]
public void RandomizedEditsAndExpirations()
{
using var source = new Signal<IChangeSet<Item, int>>();
using var subscription = source
.ExpireAfter(
timeSelector: static item => item.Lifetime,
pollingInterval: null)
.Subscribe();
foreach (var changeSet in _changeSets)
source.OnNext(changeSet);
subscription.Dispose();
}
private readonly ImmutableArray<IChangeSet<Item, int>> _changeSets;
private sealed record Item
{
public required int Id { get; init; }
public required TimeSpan? Lifetime { get; init; }
}
}