@@ -14,203 +14,179 @@ namespace BitFaster.Caching.UnitTests.Lfu
14
14
[ Collection ( "Soak" ) ]
15
15
public class ConcurrentLfuSoakTests
16
16
{
17
- private const int iterations = 10 ;
17
+ private const int soakIterations = 10 ;
18
+ private const int threads = 4 ;
19
+ private const int loopIterations = 100_000 ;
20
+
18
21
private readonly ITestOutputHelper output ;
19
22
public ConcurrentLfuSoakTests ( ITestOutputHelper testOutputHelper )
20
23
{
21
24
this . output = testOutputHelper ;
22
25
}
23
26
24
27
[ Theory ]
25
- [ Repeat ( iterations ) ]
28
+ [ Repeat ( soakIterations ) ]
26
29
public async Task WhenConcurrentGetCacheEndsInConsistentState ( int iteration )
27
30
{
28
- var scheduler = new BackgroundThreadScheduler ( ) ;
29
- var lfu = new ConcurrentLfuBuilder < int , string > ( ) . WithCapacity ( 9 ) . WithScheduler ( scheduler ) . Build ( ) as ConcurrentLfu < int , string > ;
31
+ var lfu = CreateWithBackgroundScheduler ( ) ;
30
32
31
- await Threaded . Run ( 4 , ( ) => {
32
- for ( int i = 0 ; i < 100000 ; i ++ )
33
+ await Threaded . Run ( threads , ( ) => {
34
+ for ( int i = 0 ; i < loopIterations ; i ++ )
33
35
{
34
36
lfu . GetOrAdd ( i + 1 , i => i . ToString ( ) ) ;
35
37
}
36
38
} ) ;
37
-
38
- this . output . WriteLine ( $ "iteration { iteration } keys={ string . Join ( " " , lfu . Keys ) } ") ;
39
-
40
- scheduler . Dispose ( ) ;
41
- await scheduler . Completion ;
42
-
43
- RunIntegrityCheck ( lfu ) ;
39
+
40
+ await RunIntegrityCheckAsync ( lfu , iteration ) ;
44
41
}
45
42
46
43
[ Theory ]
47
- [ Repeat ( iterations ) ]
44
+ [ Repeat ( soakIterations ) ]
48
45
public async Task WhenConcurrentGetAsyncCacheEndsInConsistentState ( int iteration )
49
46
{
50
- var scheduler = new BackgroundThreadScheduler ( ) ;
51
- var lfu = new ConcurrentLfuBuilder < int , string > ( ) . WithCapacity ( 9 ) . WithScheduler ( scheduler ) . Build ( ) as ConcurrentLfu < int , string > ;
47
+ var lfu = CreateWithBackgroundScheduler ( ) ;
52
48
53
- await Threaded . RunAsync ( 4 , async ( ) => {
54
- for ( int i = 0 ; i < 100000 ; i ++ )
49
+ await Threaded . RunAsync ( threads , async ( ) => {
50
+ for ( int i = 0 ; i < loopIterations ; i ++ )
55
51
{
56
52
await lfu . GetOrAddAsync ( i + 1 , i => Task . FromResult ( i . ToString ( ) ) ) ;
57
53
}
58
- } ) ;
59
-
60
- this . output . WriteLine ( $ "iteration { iteration } keys={ string . Join ( " " , lfu . Keys ) } ") ;
61
-
62
- scheduler . Dispose ( ) ;
63
- await scheduler . Completion ;
54
+ } ) ;
64
55
65
- RunIntegrityCheck ( lfu ) ;
56
+ await RunIntegrityCheckAsync ( lfu , iteration ) ;
66
57
}
67
58
68
59
[ Theory ]
69
- [ Repeat ( iterations ) ]
60
+ [ Repeat ( soakIterations ) ]
70
61
public async Task WhenConcurrentGetWithArgCacheEndsInConsistentState ( int iteration )
71
62
{
72
- var scheduler = new BackgroundThreadScheduler ( ) ;
73
- var lfu = new ConcurrentLfuBuilder < int , string > ( ) . WithCapacity ( 9 ) . WithScheduler ( scheduler ) . Build ( ) as ConcurrentLfu < int , string > ;
63
+ var lfu = CreateWithBackgroundScheduler ( ) ;
74
64
75
- await Threaded . Run ( 4 , ( ) => {
76
- for ( int i = 0 ; i < 100000 ; i ++ )
65
+ await Threaded . Run ( threads , ( ) => {
66
+ for ( int i = 0 ; i < loopIterations ; i ++ )
77
67
{
78
68
// use the arg overload
79
69
lfu . GetOrAdd ( i + 1 , ( i , s ) => i . ToString ( ) , "Foo" ) ;
80
70
}
81
71
} ) ;
82
72
83
- this . output . WriteLine ( $ "iteration { iteration } keys={ string . Join ( " " , lfu . Keys ) } ") ;
84
-
85
- scheduler . Dispose ( ) ;
86
- await scheduler . Completion ;
87
-
88
- RunIntegrityCheck ( lfu ) ;
73
+ await RunIntegrityCheckAsync ( lfu , iteration ) ;
89
74
}
90
75
91
76
[ Theory ]
92
- [ Repeat ( iterations ) ]
77
+ [ Repeat ( soakIterations ) ]
93
78
public async Task WhenConcurrentGetAsyncWithArgCacheEndsInConsistentState ( int iteration )
94
79
{
95
- var scheduler = new BackgroundThreadScheduler ( ) ;
96
- var lfu = new ConcurrentLfuBuilder < int , string > ( ) . WithCapacity ( 9 ) . WithScheduler ( scheduler ) . Build ( ) as ConcurrentLfu < int , string > ;
80
+ var lfu = CreateWithBackgroundScheduler ( ) ;
97
81
98
- await Threaded . RunAsync ( 4 , async ( ) => {
99
- for ( int i = 0 ; i < 100000 ; i ++ )
82
+ await Threaded . RunAsync ( threads , async ( ) => {
83
+ for ( int i = 0 ; i < loopIterations ; i ++ )
100
84
{
101
85
// use the arg overload
102
86
await lfu . GetOrAddAsync ( i + 1 , ( i , s ) => Task . FromResult ( i . ToString ( ) ) , "Foo" ) ;
103
87
}
104
88
} ) ;
105
89
106
- this . output . WriteLine ( $ "iteration { iteration } keys={ string . Join ( " " , lfu . Keys ) } ") ;
107
-
108
- scheduler . Dispose ( ) ;
109
- await scheduler . Completion ;
110
-
111
- RunIntegrityCheck ( lfu ) ;
90
+ await RunIntegrityCheckAsync ( lfu , iteration ) ;
112
91
}
113
92
114
93
[ Theory ]
115
- [ Repeat ( iterations ) ]
94
+ [ Repeat ( soakIterations ) ]
116
95
public async Task WhenConcurrentGetAndUpdateCacheEndsInConsistentState ( int iteration )
117
96
{
118
- var scheduler = new BackgroundThreadScheduler ( ) ;
119
- var lfu = new ConcurrentLfuBuilder < int , string > ( ) . WithCapacity ( 9 ) . WithScheduler ( scheduler ) . Build ( ) as ConcurrentLfu < int , string > ;
97
+ var lfu = CreateWithBackgroundScheduler ( ) ;
120
98
121
- await Threaded . Run ( 4 , ( ) => {
122
- for ( int i = 0 ; i < 100000 ; i ++ )
99
+ await Threaded . Run ( threads , ( ) => {
100
+ for ( int i = 0 ; i < loopIterations ; i ++ )
123
101
{
124
102
lfu . TryUpdate ( i + 1 , i . ToString ( ) ) ;
125
103
lfu . GetOrAdd ( i + 1 , i => i . ToString ( ) ) ;
126
104
}
127
105
} ) ;
128
106
129
- this . output . WriteLine ( $ "iteration { iteration } keys={ string . Join ( " " , lfu . Keys ) } ") ;
130
-
131
- scheduler . Dispose ( ) ;
132
- await scheduler . Completion ;
133
-
134
- RunIntegrityCheck ( lfu ) ;
107
+ await RunIntegrityCheckAsync ( lfu , iteration ) ;
135
108
}
136
109
137
110
[ Theory ]
138
- [ Repeat ( iterations ) ]
111
+ [ Repeat ( soakIterations ) ]
139
112
public async Task WhenSoakConcurrentGetAndRemoveCacheEndsInConsistentState ( int iteration )
140
113
{
141
- var scheduler = new BackgroundThreadScheduler ( ) ;
142
- var lfu = new ConcurrentLfuBuilder < int , string > ( ) . WithCapacity ( 9 ) . WithScheduler ( scheduler ) . Build ( ) as ConcurrentLfu < int , string > ;
114
+ var lfu = CreateWithBackgroundScheduler ( ) ;
143
115
144
- await Threaded . Run ( 4 , ( ) => {
145
- for ( int i = 0 ; i < 100000 ; i ++ )
116
+ await Threaded . Run ( threads , ( ) => {
117
+ for ( int i = 0 ; i < loopIterations ; i ++ )
146
118
{
147
119
lfu . TryRemove ( i + 1 ) ;
148
120
lfu . GetOrAdd ( i + 1 , i => i . ToString ( ) ) ;
149
121
}
150
122
} ) ;
151
123
152
- this . output . WriteLine ( $ "iteration { iteration } keys={ string . Join ( " " , lfu . Keys ) } ") ;
153
-
154
- scheduler . Dispose ( ) ;
155
- await scheduler . Completion ;
156
-
157
- RunIntegrityCheck ( lfu ) ;
124
+ await RunIntegrityCheckAsync ( lfu , iteration ) ;
158
125
}
159
126
160
127
[ Theory ]
161
- [ Repeat ( iterations ) ]
128
+ [ Repeat ( soakIterations ) ]
162
129
public async Task WhenConcurrentGetAndRemoveKvpCacheEndsInConsistentState ( int iteration )
163
130
{
164
- var scheduler = new BackgroundThreadScheduler ( ) ;
165
- var lfu = new ConcurrentLfuBuilder < int , string > ( ) . WithCapacity ( 9 ) . WithScheduler ( scheduler ) . Build ( ) as ConcurrentLfu < int , string > ;
131
+ var lfu = CreateWithBackgroundScheduler ( ) ;
166
132
167
- await Threaded . Run ( 4 , ( ) => {
168
- for ( int i = 0 ; i < 100000 ; i ++ )
133
+ await Threaded . Run ( threads , ( ) => {
134
+ for ( int i = 0 ; i < loopIterations ; i ++ )
169
135
{
170
136
lfu . TryRemove ( new KeyValuePair < int , string > ( i + 1 , ( i + 1 ) . ToString ( ) ) ) ;
171
137
lfu . GetOrAdd ( i + 1 , i => i . ToString ( ) ) ;
172
138
}
173
139
} ) ;
174
140
175
- this . output . WriteLine ( $ "iteration { iteration } keys={ string . Join ( " " , lfu . Keys ) } ") ;
176
-
177
- scheduler . Dispose ( ) ;
178
- await scheduler . Completion ;
179
-
180
- RunIntegrityCheck ( lfu ) ;
141
+ await RunIntegrityCheckAsync ( lfu , iteration ) ;
181
142
}
182
143
183
144
[ Fact ]
184
145
public async Task ThreadedVerifyMisses ( )
185
146
{
186
147
// buffer size is 1, this will cause dropped writes on some threads where the buffer is full
187
- var cache = new ConcurrentLfu < int , int > ( 1 , 20 , new NullScheduler ( ) , EqualityComparer < int > . Default ) ;
188
-
189
- int threads = 4 ;
190
- int iterations = 100_000 ;
148
+ var cache = new ConcurrentLfu < int , string > ( 1 , 20 , new NullScheduler ( ) , EqualityComparer < int > . Default ) ;
191
149
192
150
await Threaded . Run ( threads , i =>
193
151
{
194
- Func < int , int > func = x => x ;
152
+ Func < int , string > func = x => x . ToString ( ) ;
195
153
196
- int start = i * iterations ;
154
+ int start = i * loopIterations ;
197
155
198
- for ( int j = start ; j < start + iterations ; j ++ )
156
+ for ( int j = start ; j < start + loopIterations ; j ++ )
199
157
{
200
158
cache . GetOrAdd ( j , func ) ;
201
159
}
202
160
} ) ;
203
161
204
- var samplePercent = cache . Metrics . Value . Misses / ( double ) iterations / threads * 100 ;
162
+ var samplePercent = cache . Metrics . Value . Misses / ( double ) loopIterations / threads * 100 ;
205
163
206
164
this . output . WriteLine ( $ "Cache misses { cache . Metrics . Value . Misses } (sampled { samplePercent } %)") ;
207
165
this . output . WriteLine ( $ "Maintenance ops { cache . Scheduler . RunCount } ") ;
208
166
209
- cache . Metrics . Value . Misses . Should ( ) . Be ( iterations * threads ) ;
167
+ cache . Metrics . Value . Misses . Should ( ) . Be ( loopIterations * threads ) ;
210
168
RunIntegrityCheck ( cache ) ;
211
- }
169
+ }
170
+
171
+ private ConcurrentLfu < int , string > CreateWithBackgroundScheduler ( )
172
+ {
173
+ var scheduler = new BackgroundThreadScheduler ( ) ;
174
+ return new ConcurrentLfuBuilder < int , string > ( ) . WithCapacity ( 9 ) . WithScheduler ( scheduler ) . Build ( ) as ConcurrentLfu < int , string > ;
175
+ }
176
+
177
+ private async Task RunIntegrityCheckAsync ( ConcurrentLfu < int , string > lfu , int iteration )
178
+ {
179
+ this . output . WriteLine ( $ "iteration { iteration } keys={ string . Join ( " " , lfu . Keys ) } ") ;
180
+
181
+ var scheduler = lfu . Scheduler as BackgroundThreadScheduler ;
182
+ scheduler . Dispose ( ) ;
183
+ await scheduler . Completion ;
184
+
185
+ RunIntegrityCheck ( lfu ) ;
186
+ }
187
+
212
188
213
- private void RunIntegrityCheck < K , V > ( ConcurrentLfu < K , V > cache )
189
+ private static void RunIntegrityCheck < K , V > ( ConcurrentLfu < K , V > cache )
214
190
{
215
191
new ConcurrentLfuIntegrityChecker < K , V > ( cache ) . Validate ( ) ;
216
192
}
0 commit comments