@@ -253,44 +253,68 @@ public void AllocationQuantumIsNotAnIssueForNetCore21Plus(IToolchain toolchain)
253
253
254
254
public class MultiThreadedAllocation
255
255
{
256
- public const int Size = 1_000_000 ;
256
+ public const int Size = 1024 ;
257
257
public const int ThreadsCount = 10 ;
258
258
259
+ // We cache the threads in GlobalSetup and reuse them for each benchmark invocation
260
+ // to avoid measuring the cost of thread start and join, which varies across different runtimes.
259
261
private Thread [ ] threads ;
262
+ private volatile bool keepRunning = true ;
263
+ private readonly Barrier barrier = new ( ThreadsCount + 1 ) ;
264
+ private readonly CountdownEvent countdownEvent = new ( ThreadsCount ) ;
260
265
261
- [ IterationSetup ]
262
- public void SetupIteration ( )
266
+ [ GlobalSetup ]
267
+ public void Setup ( )
263
268
{
264
269
threads = Enumerable . Range ( 0 , ThreadsCount )
265
- . Select ( _ => new Thread ( ( ) => GC . KeepAlive ( new byte [ Size ] ) ) )
270
+ . Select ( _ => new Thread ( ( ) =>
271
+ {
272
+ while ( keepRunning )
273
+ {
274
+ barrier . SignalAndWait ( ) ;
275
+ GC . KeepAlive ( new byte [ Size ] ) ;
276
+ countdownEvent . Signal ( ) ;
277
+ }
278
+ } ) )
266
279
. ToArray ( ) ;
280
+ foreach ( var thread in threads )
281
+ {
282
+ thread . Start ( ) ;
283
+ }
267
284
}
268
285
269
- [ Benchmark ]
270
- public void Allocate ( )
286
+ [ GlobalCleanup ]
287
+ public void Cleanup ( )
271
288
{
289
+ keepRunning = false ;
290
+ barrier . SignalAndWait ( ) ;
272
291
foreach ( var thread in threads )
273
292
{
274
- thread . Start ( ) ;
275
293
thread . Join ( ) ;
276
294
}
277
295
}
296
+
297
+ [ Benchmark ]
298
+ public void Allocate ( )
299
+ {
300
+ countdownEvent . Reset ( ThreadsCount ) ;
301
+ barrier . SignalAndWait ( ) ;
302
+ countdownEvent . Wait ( ) ;
303
+ }
278
304
}
279
305
280
- [ Theory ( Skip = "Test is currently failing on all toolchains." ) ]
306
+ [ TheoryEnvSpecific ( "Full Framework cannot measure precisely enough" , EnvRequirement . DotNetCoreOnly ) ]
281
307
[ MemberData ( nameof ( GetToolchains ) ) ]
282
308
[ Trait ( Constants . Category , Constants . BackwardCompatibilityCategory ) ]
283
309
public void MemoryDiagnoserIsAccurateForMultiThreadedBenchmarks ( IToolchain toolchain )
284
310
{
285
311
long objectAllocationOverhead = IntPtr . Size * 2 ; // pointer to method table + object header word
286
312
long arraySizeOverhead = IntPtr . Size ; // array length
287
313
long memoryAllocatedPerArray = ( MultiThreadedAllocation . Size + objectAllocationOverhead + arraySizeOverhead ) ;
288
- long threadStartAndJoinOverhead = 112 ; // this is more or less a magic number taken from memory profiler
289
- long allocatedMemoryPerThread = memoryAllocatedPerArray + threadStartAndJoinOverhead ;
290
314
291
315
AssertAllocations ( toolchain , typeof ( MultiThreadedAllocation ) , new Dictionary < string , long >
292
316
{
293
- { nameof ( MultiThreadedAllocation . Allocate ) , allocatedMemoryPerThread * MultiThreadedAllocation . ThreadsCount }
317
+ { nameof ( MultiThreadedAllocation . Allocate ) , memoryAllocatedPerArray * MultiThreadedAllocation . ThreadsCount }
294
318
} ) ;
295
319
}
296
320
0 commit comments