GitXplorerGitXplorer
t

ProtoBenchmarkHelpers

public
9 stars
0 forks
0 issues

Commits

List of commits on branch main.
Unverified
d882ae6707a325c95aa18711a11f229da2841ed6

Configure the ValueTask to not capture the context.

ttimcassell committed 5 months ago
Unverified
237780624075221217267595f60adf606ec1d289

Removed maxConcurrency limit.

ttimcassell committed 5 months ago
Verified
bc7e7a9b8842ae0755f01d8f575f7328c1ba7841

Merge pull request #2 from timcassell/asyncbenchmarkthreadhelper

ttimcassell committed 2 years ago
Unverified
e8c39e49c9c1f376c32bd533af0675e8c367f1e1

Added `AsyncBenchmarkThreadHelper`.

ttimcassell committed 2 years ago
Unverified
f439d3207f48131f6eb608cec708fa3594d74016

Fixed threads preventing GC if the `BenchmarkThreadHelper` is never disposed.

ttimcassell committed 2 years ago
Unverified
ab615bcda4f55ea771eda6bad08ffd826e0575d0

Throw ObjectDisposedException instead of InvalidOperationException.

ttimcassell committed 2 years ago

README

The README file for this repository.

ProtoBenchmarkHelpers

Helpers for BenchmarkDotNet.

BenchmarkThreadHelper

Useful for benchmarking actions ran on multiple threads concurrently more accurately than other solutions.

It can be the same action ran multiple times in parallel, like incrementing a counter. Or it can be different actions ran at the same time in parallel, like reading from and writing to a concurrent collection.

Example, to compare the performance of incrementing a counter using Interlocked vs with a lock:

public class ThreadBenchmarks
{
    private BenchmarkThreadHelper threadHelper;

    private readonly object locker = new();
    private int counter;

    [GlobalCleanup]
    public void Cleanup()
    {
        threadHelper.Dispose();
    }

    [GlobalSetup(Target = nameof(ThreadHelperInterlocked))]
    public void SetupInterlocked()
    {
        Action action = () => Interlocked.Increment(ref counter);
        threadHelper = new BenchmarkThreadHelper()
        {
            action,
            action,
            action,
            action
        };
    }

    [Benchmark]
    public void ThreadHelperInterlocked()
    {
        threadHelper.ExecuteAndWait();
    }

    [GlobalSetup(Target = nameof(ThreadHelperLocked))]
    public void SetupLocked()
    {
        Action action = () =>
        {
            unchecked
            {
                lock (locker)
                {
                    ++counter;
                }
            }
        };
        threadHelper = new BenchmarkThreadHelper()
        {
            action,
            action,
            action,
            action
        };
    }

    [Benchmark]
    public void ThreadHelperLocked()
    {
        threadHelper.ExecuteAndWait();
    }
}

Compare to System.Threading.Tasks.Parallel:

Method MaxConcurrency Mean Error StdDev Allocated
ParallelOverhead -1 4,086.4 ns 78.81 ns 80.93 ns 514 B
ParallelInterlocked -1 4,370.0 ns 86.95 ns 147.65 ns 517 B
ParallelLocked -1 4,798.6 ns 54.14 ns 50.64 ns 522 B
ThreadHelperOverhead -1 1,172.7 ns 3.55 ns 3.32 ns -
ThreadHelperInterlocked -1 1,302.8 ns 2.07 ns 1.83 ns -
ThreadHelperLocked -1 1,660.2 ns 4.42 ns 4.13 ns -
ParallelOverhead 2 2,843.1 ns 19.03 ns 17.80 ns 1288 B
ParallelInterlocked 2 2,737.6 ns 20.99 ns 19.63 ns 1288 B
ParallelLocked 2 2,805.0 ns 18.68 ns 17.48 ns 1288 B
ThreadHelperOverhead 2 602.1 ns 2.39 ns 2.23 ns -
ThreadHelperInterlocked 2 697.4 ns 2.77 ns 2.59 ns -
ThreadHelperLocked 2 992.7 ns 2.93 ns 2.74 ns -

AsyncBenchmarkThreadHelper

Very similar to BenchmarkThreadHelper, except AsyncBenchmarkThreadHelper supports async actions.

public class ThreadAsyncBenchmarks
{
    private const int numTasks = 2;

    [ParamsAllValues]
    public bool Yield { get; set; }

    private AsyncBenchmarkThreadHelper asyncThreadHelper;

    private async ValueTask FuncAsync()
    {
        if (Yield)
            await Task.Yield();
    }

    [GlobalSetup(Target = nameof(AsyncThreadHelper))]
    public void SetupAsyncThreadHelper()
    {
        asyncThreadHelper = new();
        for (int i = 0; i < numTasks; ++i)
        {
            asyncThreadHelper.Add(() => FuncAsync());
        }
    }

    [Benchmark]
    public ValueTask AsyncThreadHelper()
    {
        return asyncThreadHelper.ExecuteAndWaitAsync();
    }
}

Compare to Task.Run and Task.WhenAll:

Method Yield Mean Error StdDev Allocated
TaskRun False 3,568.3 ns 17.02 ns 15.92 ns 408 B
AsyncThreadHelper False 833.6 ns 2.97 ns 2.48 ns -
TaskRun True 5,769.1 ns 109.06 ns 112.00 ns 648 B
AsyncThreadHelper True 3,666.5 ns 10.15 ns 9.00 ns 240 B