BenchmarkDotNet
BenchmarkDotNet copied to clipboard
The way to benchmark multithreaded actions.
Consider some object, which should hold a lot of concurrent operations at the same time. There is the need to benchmark one operation from one thread (still having others working) to verify productivity during parallel load. The problem is how to correctly hold that background threads still measuring one needed.
I see some ways, but none of them are completely possible now.
- An ability to run benchmark methods in parallel, so every thread runs its own measuring still providing the needed concurrency.
- Establish background in setup method. Unfortunatly, there is a need of "unsetup" method, where threads should be stopped.
@kayanme, this is a very great idea. We definitely should add this feature. Unfortunately, it is hard to implement right way. There are a lot of multi-threading side effects like false sharing which can easily spoil a benchmark. We will add it sometimes, but not in the nearest future.
danielpalme/IocPerformance#77
Just a big +1 to adding support for properly benchmarking in a concurrent environment - it's super important to be able to see how code behaves under concurrent load (lock contention, CPU caches...). Some knobs would be necessary - number of threads etc.
+1
This should be one of the top priorities, please.
+1
+1
While not an exact replacement, with the deprecation of the Visual Studio load tests, it would be great to be able to migrate some the loads tests to benchmarks and get some similar data out of it (customize # of 'users'/threads, time per run, etc.).
Any news/thoughts on this?
I have simpler scenario where i want to benchmark operations like Add
on ConcurrentDictionary
like collections, parameterizing the number of threads. i.e. how does this collection's a particular operation scale with the concurrent requests ? For inspiration, google's benchmark library support this.
+1
+1
+1
Would it make sense to have a helper class to do this, rather than attributes and code-gen?
class Benchmark
{
private ThreadHelper threadHelper;
[GlobalSetup]
public void Setup()
{
threadHelper = new ThreadHelper(
() => { ... },
maxConcurrency
);
}
[Benchmark]
public void Multithread()
{
...
threadHelper.Execute();
}
}
I've done something like this for concurrency tests during unit testing, so I could do it again for this if it makes sense, but we'd need to settle on a good API.
[Edit] This would only solve measuring concurrent actions. Running benchmarks in parallel is a separate beast.
I wrote a helper to make it simpler and more efficient to benchmark multi-threaded actions than manually setting up threads. https://github.com/timcassell/ProtoBenchmarkHelpers This can be used until BDN adds multi-threaded benchmarking officially.
@timcassell does your helper work for async actions? The generation of async state machines seems to wreak havoc on all BDN benchmarking and while your helper is great for running multiple threads in parallel, it doesn't seem to be sufficient for async methods. Either way, thanks for your work, it is fantastic!
@Stabzs I did not have async in mind when I wrote that. I'm not sure adding async support would be of much use, though. If your async actions hop to different threads while they're running, all the benefits of the custom thread management goes out the window, and you might as well just use Task.Run
and Task.WhenAll
. Do you have a specific use-case in mind?
@timcassell thanks for the quick response!
There's a handful of scenarios that I'd like to benchmark where I need to run multiple concurrent tasks to simulate contention across threads, such as the memory cost of asynchronously waiting on a potentially contended SemaphoreSlim
instance. Some optimizations (pooling, ValueTask, etc etc), can have significant allocation reductions. However, the overhead from arrays of tasks and Task.Run
skew the allocations of the asynchronous workers themselves, making it difficult to measure performance.
In addition, both allocations and elapsed time tend to be very sporadic when benchmarking async methods.
Overall, it doesn't seem that there is a great way to benchmark async methods. I've used your helper before, but in hindsight, it probably is the wrong approach.
In addition, both allocations and elapsed time tend to be very sporadic when benchmarking async methods.
Well that's not surprising if they're hopping to different threads.
Some optimizations (pooling, ValueTask, etc etc), can have significant allocation reductions. However, the overhead from arrays of tasks and
Task.Run
skew the allocations of the asynchronous workers themselves, making it difficult to measure performance.
That's a good point. I have an idea how to support this. I'll open a new issue on that repo for this.
@Stabzs In the meantime, you could try synchronously waiting on the tasks inside the actions. task.GetAwaiter().GetResult()
. That's how BDN currently consumes tasks anyway.
@timcassell I appreciate the suggestion! I'll give that a try and see if it changes the results.
No-one has suggested what the API for this should look like yet, so here's my idea.
public class Benchmarks
{
[ParamsAllValues]
public bool MyFlag { get; set; }
[Benchmark]
public void SingleThread() { }
[ThreadedBenchmark(MaxConcurrency = 2)] // Default MaxConcurrency is Environment.ProcessorCount
public class MultiThread : Benchmarks // Inherit from the parent type to have access to the same params as regular benchmarks
{
[ThreadInvoke] // Invoke once on a thread
public void Action() { }
[ThreadInvoke(3)] // Invoke 3 times, each on a different thread
public int Func() => 42;
// Support the same return types as regular benchmark methods
[ThreadInvoke]
public async Task AsyncAction() { }
}
}
That would produce a table like this:
Method | MyFlag | Mean |
---|---|---|
SingleThread | False | 0.00 ns |
MultiThread | False | 0.00 ns |
SingleThread | True | 0.00 ns |
MultiThread | True | 0.00 ns |
This encapsulates the threaded actions within a child class (even though the result table labels it as a Method
, I think it's quite readable).
+1 We also need multi-threading benchmark very much