Skip to content

Commit 4d70191

Browse files
authored
Merge pull request #36 from dotnet-campus/t/lindexi/Benchmark
Add Benchmark
2 parents 5e45507 + add0c00 commit 4d70191

8 files changed

+461
-1
lines changed

AsyncWorkerCollection.sln

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ VisualStudioVersion = 16.0.30413.136
55
MinimumVisualStudioVersion = 15.0.26124.0
66
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AsyncWorkerCollection", "AsyncWorkerCollection\AsyncWorkerCollection.csproj", "{8A479E25-0FE9-4A67-9AED-8B7E0A46C62D}"
77
EndProject
8-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AsyncWorkerCollection.Tests", "test\AsyncWorkerCollection.Tests\AsyncWorkerCollection.Tests.csproj", "{70F29F77-FE49-45FE-9FED-7162897CB7B2}"
8+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AsyncWorkerCollection.Tests", "test\AsyncWorkerCollection.Tests\AsyncWorkerCollection.Tests.csproj", "{70F29F77-FE49-45FE-9FED-7162897CB7B2}"
9+
EndProject
10+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AsyncWorkerCollection.Benchmarks", "test\AsyncWorkerCollection.Benchmarks\AsyncWorkerCollection.Benchmarks.csproj", "{751CD521-7831-4C82-A2A0-D4A5FDC8D3F5}"
911
EndProject
1012
Global
1113
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -41,6 +43,18 @@ Global
4143
{70F29F77-FE49-45FE-9FED-7162897CB7B2}.Release|x64.Build.0 = Release|Any CPU
4244
{70F29F77-FE49-45FE-9FED-7162897CB7B2}.Release|x86.ActiveCfg = Release|Any CPU
4345
{70F29F77-FE49-45FE-9FED-7162897CB7B2}.Release|x86.Build.0 = Release|Any CPU
46+
{751CD521-7831-4C82-A2A0-D4A5FDC8D3F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
47+
{751CD521-7831-4C82-A2A0-D4A5FDC8D3F5}.Debug|Any CPU.Build.0 = Debug|Any CPU
48+
{751CD521-7831-4C82-A2A0-D4A5FDC8D3F5}.Debug|x64.ActiveCfg = Debug|Any CPU
49+
{751CD521-7831-4C82-A2A0-D4A5FDC8D3F5}.Debug|x64.Build.0 = Debug|Any CPU
50+
{751CD521-7831-4C82-A2A0-D4A5FDC8D3F5}.Debug|x86.ActiveCfg = Debug|Any CPU
51+
{751CD521-7831-4C82-A2A0-D4A5FDC8D3F5}.Debug|x86.Build.0 = Debug|Any CPU
52+
{751CD521-7831-4C82-A2A0-D4A5FDC8D3F5}.Release|Any CPU.ActiveCfg = Release|Any CPU
53+
{751CD521-7831-4C82-A2A0-D4A5FDC8D3F5}.Release|Any CPU.Build.0 = Release|Any CPU
54+
{751CD521-7831-4C82-A2A0-D4A5FDC8D3F5}.Release|x64.ActiveCfg = Release|Any CPU
55+
{751CD521-7831-4C82-A2A0-D4A5FDC8D3F5}.Release|x64.Build.0 = Release|Any CPU
56+
{751CD521-7831-4C82-A2A0-D4A5FDC8D3F5}.Release|x86.ActiveCfg = Release|Any CPU
57+
{751CD521-7831-4C82-A2A0-D4A5FDC8D3F5}.Release|x86.Build.0 = Release|Any CPU
4458
EndGlobalSection
4559
GlobalSection(SolutionProperties) = preSolution
4660
HideSolutionNode = FALSE

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@ Waiting for the task to dequeue:
6767
var fooTask = await asyncQueue.DequeueAsync();
6868
```
6969

70+
## Benchmark
71+
72+
See [Benchmark.md](docs/Benchmark.md)
73+
7074
## Contributing
7175

7276
[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](https://github.com/dotnet-campus/AsyncWorkerCollection/pulls)

docs/Benchmark.md

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# Benchmark
2+
3+
## Environment
4+
5+
``` ini
6+
7+
BenchmarkDotNet=v0.12.0, OS=Windows 10.0.19041
8+
Intel Core i7-6700 CPU 3.40GHz (Skylake), 1 CPU, 8 logical and 4 physical cores
9+
.NET Core SDK=3.1.402
10+
[Host] : .NET Core 3.1.8 (CoreCLR 4.700.20.41105, CoreFX 4.700.20.41903), X64 RyuJIT [AttachedDebugger]
11+
12+
Job=InProcess Toolchain=InProcessEmitToolchain
13+
14+
```
15+
16+
## Write and Read
17+
18+
| Method | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated |
19+
|-------------------------------- |---------:|--------:|--------:|------:|--------:|--------:|-------:|------:|----------:|
20+
| AsyncQueueEnqueueAndDequeueTest | 285.4 us | 5.54 us | 6.59 us | 2.22 | 0.06 | 26.3672 | 0.4883 | - | 104.51 KB |
21+
| ChannelReadAndWriteTest | 128.4 us | 2.55 us | 2.39 us | 1.00 | 0.00 | 4.1504 | - | - | 17.15 KB |
22+
23+
## Write Only
24+
25+
### AsyncQueue
26+
27+
| Method | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated |
28+
|----------------------------------------------- |-----------:|---------:|---------:|------:|--------:|--------:|-------:|------:|----------:|
29+
| DoubleBufferTaskReadAndWrite | 2,995.9 us | 55.31 us | 54.32 us | 22.89 | 0.82 | 15.6250 | - | - | 70.68 KB |
30+
| DoubleBufferTaskWithCapacityReadAndWrite | 3,005.9 us | 59.24 us | 74.92 us | 22.96 | 0.72 | 19.5313 | - | - | 86.29 KB |
31+
| AsyncQueueEnqueueAndDequeueTest | 141.6 us | 2.76 us | 3.68 us | 1.08 | 0.03 | 25.1465 | 2.4414 | - | 103.53 KB |
32+
| AsyncQueueEnqueueAndDequeueTestWithMultiThread | 284.4 us | 5.63 us | 7.52 us | 2.17 | 0.07 | 26.3672 | 2.4414 | - | 104.55 KB |
33+
| ChannelReadAndWriteTest | 130.9 us | 2.55 us | 3.41 us | 1.00 | 0.00 | 4.1504 | - | - | 17.15 KB |
34+
| ChannelReadAndWriteTestWithMultiThread | 273.9 us | 4.98 us | 4.66 us | 2.10 | 0.07 | 3.9063 | - | - | 17.83 KB |
35+
36+
37+
### DoubleBufferTask
38+
39+
| Method | threadCount | Mean | Error | StdDev | Ratio | RatioSD | Gen 0 | Gen 1 | Gen 2 | Allocated |
40+
|-------------------------------------------- |------------ |-----------:|---------:|---------:|------:|--------:|--------:|-------:|------:|----------:|
41+
| DoubleBufferTaskReadAndWrite | ? | 2,895.6 us | 39.62 us | 37.06 us | 22.65 | 0.46 | 15.6250 | - | - | 70.68 KB |
42+
| DoubleBufferTaskWithCapacityReadAndWrite | ? | 2,914.2 us | 50.76 us | 47.48 us | 22.80 | 0.42 | 19.5313 | - | - | 86.29 KB |
43+
| AsyncQueueEnqueueAndDequeueTest | ? | 275.4 us | 5.35 us | 5.73 us | 2.15 | 0.05 | 26.3672 | 0.4883 | - | 104.48 KB |
44+
| ChannelReadAndWriteTest | ? | 127.8 us | 2.06 us | 1.92 us | 1.00 | 0.00 | 4.1504 | - | - | 17.15 KB |
45+
| | | | | | | | | | | |
46+
| **DoubleBufferTaskWithMultiThreadReadAndWrite** | **2** | **2,068.5 us** | **40.93 us** | **58.70 us** | **?** | **?** | **19.5313** | **-** | **-** | **87.7 KB** |
47+
| | | | | | | | | | | |
48+
| **DoubleBufferTaskWithMultiThreadReadAndWrite** | **5** | **1,193.8 us** | **23.31 us** | **39.59 us** | **?** | **?** | **21.4844** | **-** | **-** | **88.33 KB** |
49+
| | | | | | | | | | | |
50+
| **DoubleBufferTaskWithMultiThreadReadAndWrite** | **10** | **1,120.2 us** | **22.31 us** | **28.21 us** | **?** | **?** | **21.4844** | **-** | **-** | **89.38 KB** |
51+
52+
### DoubleBufferTask with Batch task
53+
54+
| Method | Mean | Error | StdDev | Ratio | Gen 0 | Gen 1 | Gen 2 | Allocated |
55+
|------------------------------------------------ |-------------:|----------:|----------:|------:|------:|------:|------:|----------:|
56+
| DoubleBufferTaskReadAndWriteTestWithMultiThread | 31.50 ms | 0.597 ms | 0.587 ms | 0.002 | - | - | - | 90.67 KB |
57+
| ChannelReadAndWriteTestWithMultiThread | 15,791.17 ms | 43.934 ms | 41.095 ms | 1.000 | - | - | - | 645.11 KB |
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>netcoreapp3.1</TargetFramework>
6+
</PropertyGroup>
7+
8+
<ItemGroup>
9+
<PackageReference Include="BenchmarkDotNet" Version="0.12.0" />
10+
</ItemGroup>
11+
12+
<ItemGroup>
13+
<ProjectReference Include="..\..\AsyncWorkerCollection\AsyncWorkerCollection.csproj" />
14+
</ItemGroup>
15+
16+
</Project>
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Threading.Tasks;
4+
using BenchmarkDotNet.Attributes;
5+
using dotnetCampus.Threading;
6+
7+
namespace AsyncWorkerCollection.Benchmarks
8+
{
9+
/// <summary>
10+
/// 批量任务的双缓存性能对比
11+
/// </summary>
12+
[BenchmarkCategory(nameof(BatchProducerAndConsumerDoubleBufferReadAndWriteTests))]
13+
public class BatchProducerAndConsumerDoubleBufferReadAndWriteTests
14+
{
15+
[Benchmark()]
16+
public async Task DoubleBufferTaskReadAndWriteTestWithMultiThread()
17+
{
18+
const int threadCount = 1;
19+
20+
var doubleBufferTask = new DoubleBufferTask<List<Foo>, Foo>(new List<Foo>(MaxCount),
21+
new List<Foo>(MaxCount), async list =>
22+
{
23+
await StartDo();
24+
});
25+
var foo = new Foo();
26+
27+
var taskList = new Task[threadCount];
28+
29+
for (int j = 0; j < threadCount; j++)
30+
{
31+
var task = Task.Run(() =>
32+
{
33+
for (int i = 0; i < MaxCount / threadCount; i++)
34+
{
35+
doubleBufferTask.AddTask(foo);
36+
}
37+
});
38+
taskList[j] = task;
39+
}
40+
41+
await Task.WhenAll(taskList);
42+
43+
doubleBufferTask.Finish();
44+
await doubleBufferTask.WaitAllTaskFinish();
45+
}
46+
47+
[Benchmark(Baseline = true)]
48+
public async Task ChannelReadAndWriteTestWithMultiThread()
49+
{
50+
var foo = new Foo();
51+
var bounded = System.Threading.Channels.Channel.CreateBounded<Foo>(MaxCount);
52+
53+
var task = Task.Run(async () =>
54+
{
55+
int n = 0;
56+
57+
await foreach (var temp in bounded.Reader.ReadAllAsync())
58+
{
59+
await StartDo();
60+
n++;
61+
if (n == MaxCount)
62+
{
63+
break;
64+
}
65+
}
66+
});
67+
68+
for (int i = 0; i < MaxCount; i++)
69+
{
70+
await bounded.Writer.WriteAsync(foo);
71+
}
72+
73+
await task;
74+
}
75+
76+
/// <summary>
77+
/// 开始执行,如文件写入等,无论是写入多少条,都需要有开始的时间
78+
/// </summary>
79+
private async Task StartDo()
80+
{
81+
await Task.Delay(TimeSpan.FromMilliseconds(10));
82+
}
83+
84+
private const int MaxCount = 1000;
85+
86+
class Foo
87+
{
88+
}
89+
}
90+
}
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
using System.Collections.Generic;
2+
using System.Threading.Tasks;
3+
using BenchmarkDotNet.Attributes;
4+
using dotnetCampus.Threading;
5+
6+
namespace AsyncWorkerCollection.Benchmarks
7+
{
8+
/// <summary>
9+
/// 生产者消费者同时进行读写的测试
10+
/// </summary>
11+
[BenchmarkCategory(nameof(ProducerAndConsumerAsyncQueueReadAndWriteTests))]
12+
public class ProducerAndConsumerAsyncQueueReadAndWriteTests
13+
{
14+
[Benchmark()]
15+
public async Task DoubleBufferTaskReadAndWrite()
16+
{
17+
var doubleBufferTask = new DoubleBufferTask<Foo>(list => Task.CompletedTask);
18+
var foo = new Foo();
19+
20+
for (int i = 0; i < MaxCount; i++)
21+
{
22+
doubleBufferTask.AddTask(foo);
23+
}
24+
25+
doubleBufferTask.Finish();
26+
await doubleBufferTask.WaitAllTaskFinish();
27+
}
28+
29+
[Benchmark()]
30+
public async Task DoubleBufferTaskWithCapacityReadAndWrite()
31+
{
32+
var doubleBufferTask = new DoubleBufferTask<List<Foo>, Foo>(new List<Foo>(MaxCount),
33+
new List<Foo>(MaxCount), list => Task.CompletedTask);
34+
var foo = new Foo();
35+
36+
for (int i = 0; i < MaxCount; i++)
37+
{
38+
doubleBufferTask.AddTask(foo);
39+
}
40+
41+
doubleBufferTask.Finish();
42+
await doubleBufferTask.WaitAllTaskFinish();
43+
}
44+
45+
[Benchmark()]
46+
[Arguments(2)]
47+
[Arguments(5)]
48+
[Arguments(10)]
49+
public async Task DoubleBufferTaskWithMultiThreadReadAndWrite(int threadCount)
50+
{
51+
var doubleBufferTask = new DoubleBufferTask<List<Foo>, Foo>(new List<Foo>(MaxCount),
52+
new List<Foo>(MaxCount), list => Task.CompletedTask);
53+
var foo = new Foo();
54+
55+
var taskList = new Task[threadCount];
56+
57+
for (int j = 0; j < threadCount; j++)
58+
{
59+
var task = Task.Run(() =>
60+
{
61+
for (int i = 0; i < MaxCount / threadCount; i++)
62+
{
63+
doubleBufferTask.AddTask(foo);
64+
}
65+
});
66+
taskList[j] = task;
67+
}
68+
69+
await Task.WhenAll(taskList);
70+
71+
doubleBufferTask.Finish();
72+
await doubleBufferTask.WaitAllTaskFinish();
73+
}
74+
75+
[Benchmark()]
76+
public async Task AsyncQueueEnqueueAndDequeueTest()
77+
{
78+
var asyncQueue = new AsyncQueue<Foo>();
79+
var foo = new Foo();
80+
81+
for (int i = 0; i < MaxCount; i++)
82+
{
83+
asyncQueue.Enqueue(foo);
84+
}
85+
86+
for (int i = 0; i < MaxCount; i++)
87+
{
88+
var temp = await asyncQueue.DequeueAsync();
89+
}
90+
}
91+
92+
[Benchmark()]
93+
public async Task AsyncQueueEnqueueAndDequeueTestWithMultiThread()
94+
{
95+
var asyncQueue = new AsyncQueue<Foo>();
96+
var foo = new Foo();
97+
var task = Task.Run(async () =>
98+
{
99+
int n = 0;
100+
while (true)
101+
{
102+
n++;
103+
if (n == MaxCount)
104+
{
105+
break;
106+
}
107+
108+
var temp = await asyncQueue.DequeueAsync();
109+
if (temp is null)
110+
{
111+
return;
112+
}
113+
}
114+
});
115+
116+
for (int i = 0; i < MaxCount; i++)
117+
{
118+
asyncQueue.Enqueue(foo);
119+
}
120+
121+
await task;
122+
}
123+
124+
[Benchmark(Baseline = true)]
125+
public async Task ChannelReadAndWriteTest()
126+
{
127+
var foo = new Foo();
128+
var bounded = System.Threading.Channels.Channel.CreateBounded<Foo>(MaxCount);
129+
130+
for (int i = 0; i < MaxCount; i++)
131+
{
132+
await bounded.Writer.WriteAsync(foo);
133+
}
134+
135+
int n = 0;
136+
137+
await foreach (var temp in bounded.Reader.ReadAllAsync())
138+
{
139+
n++;
140+
if (n == MaxCount)
141+
{
142+
break;
143+
}
144+
}
145+
}
146+
147+
[Benchmark()]
148+
public async Task ChannelReadAndWriteTestWithMultiThread()
149+
{
150+
var foo = new Foo();
151+
var bounded = System.Threading.Channels.Channel.CreateBounded<Foo>(MaxCount);
152+
153+
var task = Task.Run(async () =>
154+
{
155+
int n = 0;
156+
157+
await foreach (var temp in bounded.Reader.ReadAllAsync())
158+
{
159+
n++;
160+
if (n == MaxCount)
161+
{
162+
break;
163+
}
164+
}
165+
});
166+
167+
for (int i = 0; i < MaxCount; i++)
168+
{
169+
await bounded.Writer.WriteAsync(foo);
170+
}
171+
172+
await task;
173+
}
174+
175+
private const int MaxCount = 1000;
176+
177+
class Foo
178+
{
179+
}
180+
}
181+
}

0 commit comments

Comments
 (0)