Skip to content

Instantly share code, notes, and snippets.

@Stepami
Created February 1, 2024 21:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Stepami/17ceafbfdd91259a9821fd808e3eb08f to your computer and use it in GitHub Desktop.
Save Stepami/17ceafbfdd91259a9821fd808e3eb08f to your computer and use it in GitHub Desktop.
using System.Reflection;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
BenchmarkSwitcher switcher = new(Assembly.GetExecutingAssembly());
switcher.Run(args);
[ShortRunJob]
public class BenchmarkSimulatedIo
{
[Params(1, 10, 100)]
public int CollectionCount;
[Params(1, 10, 100, 1000)]
public int SimulatedIoDelays;
[Benchmark]
public async Task TaskWhenAll()
{
var tasks = Enumerable
.Range(0, CollectionCount)
.Select(async _ => await Task.Delay(SimulatedIoDelays))
.ToArray();
await Task.WhenAll(tasks);
}
[Benchmark]
public async Task ParallelForEach() =>
await Parallel.ForEachAsync(
Enumerable.Range(0, CollectionCount),
cancellationToken: default,
async (i, ct) => await Task.Delay(SimulatedIoDelays, ct));
}
[ShortRunJob]
public class BenchmarkSimulatedCpu
{
private int[]? _dataSet;
[Params(1, 10, 100)]
public int CollectionCount;
[Params(1000, 10_000, 100_000, 1_000_000)]
public int CpuWorkIterations;
[GlobalSetup]
public void GlobalSetup() =>
_dataSet = Enumerable.Range(0, CollectionCount).ToArray();
[Benchmark]
public async Task TaskWhenAll()
{
var tasks = _dataSet!.Select(_ =>
{
for (var i = 0; i < CpuWorkIterations; i++)
{
Random.Shared.Next();
}
return Task.CompletedTask;
}).ToArray();
await Task.WhenAll(tasks);
}
[Benchmark]
public async Task ParallelForEach() =>
await Parallel.ForEachAsync(
_dataSet!,
cancellationToken: default,
(_, ct) =>
{
for (var i = 0; i < CpuWorkIterations; i++)
{
Random.Shared.Next();
}
return ValueTask.CompletedTask;
});
}
@eternityowo
Copy link

фиксы

    [Benchmark]
    public async Task TaskWhenAllFix()
    {
        var tasks = _dataSet.Select(_ =>
        Task.Run(() =>
        {
            for (var i = 0; i < CpuWorkIterations; i++)
            {
                Random.Shared.Next();
            }

            return Task.CompletedTask;
        })).ToArray();

        await Task.WhenAll(tasks);
    }

    [Benchmark]
    public async Task TaskWhenAllFixOptPrefer()
    {
        var tasks = _dataSet
            .Select(_ =>
                Task.Factory.StartNew(
                (obj) =>
                {
                    for (var i = 0; i < CpuWorkIterations; i++)
                    {
                        Random.Shared.Next();
                    }
                },
                CancellationToken.None,
                TaskCreationOptions.PreferFairness))
            .ToArray();

        await Task.WhenAll(tasks);
    }

    [Benchmark]
    public async Task TaskWhenAllFixOptPreferLong()
    {
        var tasks = _dataSet
            .Select(_ =>
                Task.Factory.StartNew(
                (obj) =>
                {
                    for (var i = 0; i < CpuWorkIterations; i++)
                    {
                        Random.Shared.Next();
                    }
                },
                CancellationToken.None,
                TaskCreationOptions.PreferFairness | TaskCreationOptions.LongRunning))
            .ToArray();

        await Task.WhenAll(tasks);
    }

машина

BenchmarkDotNet v0.13.12, Windows 10 (10.0.19044.3086/21H2/November2021Update)
AMD Ryzen 9 5900HX with Radeon Graphics, 1 CPU, 16 logical and 8 physical cores
.NET SDK 8.0.200-preview.23624.5
  [Host] : .NET 8.0.0 (8.0.23.53103), X86 RyuJIT AVX2

Job=ShortRun  Toolchain=InProcessNoEmitToolchain  IterationCount=3
LaunchCount=1  WarmupCount=3

чтобы не мешал антивирус ( у меня)

public class AntiVirusFriendlyConfig : ManualConfig
{
    public AntiVirusFriendlyConfig()
    {
        AddJob(
               Job.ShortRun
                  .WithToolchain(InProcessNoEmitToolchain.Instance)
              )
            .WithOptions(ConfigOptions.DisableOptimizationsValidator);
    }
}

[Config(typeof(AntiVirusFriendlyConfig))]
public class BenchmarkSimulatedCpu
{
  // ...
}

результаты

Method CollectionCount CpuWorkIterations Mean, us Error, us StdDev, us
TaskWhenAll 1 1000 5,89 0,02 0,00
TaskWhenAllFix 1 1000 7,23 2,73 0,15
TaskWhenAllFixOptPreferLong 1 1000 90,00 11,68 0,64
TaskWhenAllFixOptPrefer 1 1000 6,58 1,03 0,06
ParallelForEach 1 1000 7,02 0,96 0,05
TaskWhenAll 1 10000 55,84 2,86 0,16
TaskWhenAllFix 1 10000 65,77 3,54 0,19
TaskWhenAllFixOptPreferLong 1 10000 147,41 3,21 0,18
TaskWhenAllFixOptPrefer 1 10000 60,89 3,98 0,22
ParallelForEach 1 10000 61,84 3,66 0,20
TaskWhenAll 1 100000 562,15 35,50 1,95
TaskWhenAllFix 1 100000 604,50 18,46 1,01
TaskWhenAllFixOptPreferLong 1 100000 799,51 38,46 2,11
TaskWhenAllFixOptPrefer 1 100000 554,01 15,21 0,83
ParallelForEach 1 100000 562,15 2,52 0,14
TaskWhenAll 1 1000000 5576,28 299,89 16,44
TaskWhenAllFix 1 1000000 6157,51 71,39 3,91
TaskWhenAllFixOptPreferLong 1 1000000 6726,71 40,76 2,23
TaskWhenAllFixOptPrefer 1 1000000 5799,29 465,52 25,52
ParallelForEach 1 1000000 5498,63 629,15 34,49
TaskWhenAll 10 1000 55,96 1,43 0,08
TaskWhenAllFix 10 1000 24,53 4,88 0,27
TaskWhenAllFixOptPreferLong 10 1000 676,65 25,40 1,39
TaskWhenAllFixOptPrefer 10 1000 19,46 3,11 0,17
ParallelForEach 10 1000 23,28 4,46 0,24
TaskWhenAll 10 10000 557,27 3,42 0,19
TaskWhenAllFix 10 10000 142,95 14,00 0,77
TaskWhenAllFixOptPreferLong 10 10000 770,34 235,94 12,93
TaskWhenAllFixOptPrefer 10 10000 139,31 26,17 1,43
ParallelForEach 10 10000 131,97 71,33 3,91
TaskWhenAll 10 100000 5620,51 252,79 13,86
TaskWhenAllFix 10 100000 1346,26 88,97 4,88
TaskWhenAllFixOptPreferLong 10 100000 1494,39 457,81 25,09
TaskWhenAllFixOptPrefer 10 100000 1511,58 133,75 7,33
ParallelForEach 10 100000 1287,92 118,45 6,49
TaskWhenAll 10 1000000 56083,49 901,68 49,42
TaskWhenAllFix 10 1000000 14843,04 423,21 23,20
TaskWhenAllFixOptPreferLong 10 1000000 9109,45 2712,08 148,66
TaskWhenAllFixOptPrefer 10 1000000 13776,93 949,59 52,05
ParallelForEach 10 1000000 15672,87 1585,15 86,89
TaskWhenAll 100 1000 551,44 16,77 0,92
TaskWhenAllFix 100 1000 82,71 17,05 0,93
TaskWhenAllFixOptPreferLong 100 1000 6755,16 869,79 47,68
TaskWhenAllFixOptPrefer 100 1000 78,02 29,95 1,64
ParallelForEach 100 1000 87,96 20,94 1,15
TaskWhenAll 100 10000 5598,09 145,56 7,98
TaskWhenAllFix 100 10000 746,16 277,87 15,23
TaskWhenAllFixOptPreferLong 100 10000 7168,16 6860,82 376,06
TaskWhenAllFixOptPrefer 100 10000 697,23 75,59 4,14
ParallelForEach 100 10000 743,99 599,11 32,84
TaskWhenAll 100 100000 57163,23 1051,65 57,64
TaskWhenAllFix 100 100000 7229,14 4827,13 264,59
TaskWhenAllFixOptPreferLong 100 100000 10357,65 7159,00 392,41
TaskWhenAllFixOptPrefer 100 100000 6715,82 5779,54 316,80
ParallelForEach 100 100000 7534,91 2032,86 111,43
TaskWhenAll 100 1000000 565280,80 61806,88 3387,84
TaskWhenAllFix 100 1000000 70064,28 118647,72 6503,48
TaskWhenAllFixOptPreferLong 100 1000000 85073,32 184769,092 10127,82
TaskWhenAllFixOptPrefer 100 1000000 61679,49 24578,22 1347,22
ParallelForEach 100 1000000 74319,36 45998,11 2521,31

почему работает PreferFairness, но не работает LongRunning

  • PreferFairness ставит ваши таски в общую очередь, и если какой-то тред становится свободным, то он идет в общую очередь, а не занимается Wrok Stealing из очереди другого треда.
  • LongRunning же пытается запустить под каждую таску отдельный тред, но если уходим в оверхед на их создание и малое кол-во физичиских ядер для параллелього исполнения

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment