Skip to content

Commit 1c086d0

Browse files
Add batching with IMemoryOwner<T> and related tests (#49)
* Add batching with IMemoryOwner<T> and related tests Introduced a new feature for batching results as `IMemoryOwner<T>` to improve memory management. Updated `.editorconfig` for better code style. Modified `SpecialTests.cs` to correct the expected count. Added documentation and comments in `Extensions.Batch.cs`. Updated `BatchingChannelReader.cs` with new methods and classes for batching. Added comprehensive unit tests in `BatchTests.Memory.cs` and `BatchTests.Queue.cs` to ensure the new batching methods work correctly under various conditions. * Add benchmarks for batching methods and update batch creation Added a new project `Open.ChannelExtensions.Benchmarks` to the solution, including a `BatchDrain` class to measure performance of different batching methods. Updated `Batch` extension methods to include an optional `batchFactory` parameter. Modified `BatchingChannelReader` to use `batchFactory` for batch creation. Added benchmark tests in `BatchDrain.cs` for list-based, queue-based, and memory-pooled batching methods. Updated solution file to include the new benchmark project. Added `Program.cs` to run benchmarks using `BenchmarkDotNet`. * Refactor BatchDrain and update Join methods Refactored `BatchDrain` class in `BatchDrain.cs` by removing `_noOpTarget`, `NoOp`, and `GlobalCleanup`. Updated benchmark methods to use `Join` with a `true` parameter and static lambda for `ReadAll`. Modified `BatchListDrainPooled` and `BatchQueueDrainPooled` to use `Join` with recycler action. In `Extensions.Join.cs`, updated `JoiningChannelReader` to accept an optional `recycler` action. Added `QueueJoiningChannelReader` and `MemoryJoiningChannelReader` for `Queue<T>` and `IMemoryOwner<T>`. Updated `Join` methods to support new types and optional `recycler` action. Added `TryDequeue` for `Queue<T>` to support .NET Standard 2.0.
1 parent 27a2c57 commit 1c086d0

File tree

11 files changed

+1267
-71
lines changed

11 files changed

+1267
-71
lines changed

.editorconfig

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ csharp_style_pattern_matching_over_as_with_null_check = true:warning
9393
csharp_style_pattern_matching_over_is_with_cast_check = true:warning
9494
csharp_style_prefer_not_pattern = true:warning
9595
csharp_style_prefer_pattern_matching = true:suggestion
96-
csharp_style_prefer_switch_expression = true
96+
csharp_style_prefer_switch_expression = true:suggestion
9797

9898
# Null-checking preferences
9999
csharp_style_conditional_delegate_call = true:warning
@@ -143,7 +143,7 @@ csharp_new_line_between_query_expression_clauses = true
143143
csharp_indent_block_contents = true
144144
csharp_indent_braces = false
145145
csharp_indent_case_contents = true
146-
csharp_indent_case_contents_when_block = true
146+
csharp_indent_case_contents_when_block = false
147147
csharp_indent_labels = one_less_than_current
148148
csharp_indent_switch_labels = true
149149

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
using BenchmarkDotNet.Attributes;
2+
using System.Threading.Channels;
3+
4+
namespace Open.ChannelExtensions.Benchmarks;
5+
6+
[MemoryDiagnoser]
7+
public class BatchDrain
8+
{
9+
[Params(10, 100, 500, 1000, 3000, 12000)]
10+
public int BatchSize { get; set; }
11+
12+
public int TotalItems = 10000000;
13+
14+
private Channel<int>? _channel;
15+
16+
private readonly Queue<List<int>> _listPool = new();
17+
private readonly Queue<Queue<int>> _queuePool = new();
18+
19+
[IterationSetup]
20+
public void IterationSetup()
21+
{
22+
_channel = Channel.CreateUnbounded<int>(new UnboundedChannelOptions
23+
{
24+
SingleReader = true,
25+
SingleWriter = true
26+
});
27+
28+
for (int i = 0; i < TotalItems; i++)
29+
{
30+
_channel.Writer.TryWrite(i);
31+
}
32+
33+
_channel.Writer.Complete();
34+
35+
_listPool.Clear();
36+
_queuePool.Clear();
37+
38+
_listPool.Enqueue(new List<int>(BatchSize));
39+
_listPool.Enqueue(new List<int>(BatchSize));
40+
_queuePool.Enqueue(new Queue<int>(BatchSize));
41+
_queuePool.Enqueue(new Queue<int>(BatchSize));
42+
}
43+
44+
[Benchmark(Baseline = true)]
45+
public async Task BatchListDrain()
46+
=> await _channel!.Reader
47+
.Batch(BatchSize)
48+
.Join(true)
49+
.ReadAll(static _ => { });
50+
51+
[Benchmark]
52+
public async Task BatchListDrainPooled()
53+
=> await _channel!.Reader
54+
.Batch(BatchSize, batchFactory: _ => _listPool.Dequeue())
55+
.Join(true, e =>
56+
{
57+
e.Clear(); // Simulate resetting the size.
58+
_listPool.Enqueue(e);
59+
})
60+
.ReadAll(static _ => { });
61+
62+
[Benchmark]
63+
public async Task BatchQueueDrain()
64+
=> await _channel!.Reader
65+
.BatchToQueues(BatchSize)
66+
.Join(true)
67+
.ReadAll(static _ => { });
68+
69+
[Benchmark]
70+
public async Task BatchQueueDrainPooled()
71+
=> await _channel!.Reader
72+
.BatchToQueues(BatchSize, batchFactory: _ => _queuePool.Dequeue())
73+
.Join(true, _queuePool.Enqueue)
74+
.ReadAll(static _ => { });
75+
76+
[Benchmark]
77+
public async Task BatchMemoryOwnerDrain()
78+
=> await _channel!.Reader
79+
.BatchAsMemory(BatchSize)
80+
.Join(true)
81+
.ReadAll(static _ => { });
82+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net8.0</TargetFramework>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<Nullable>enable</Nullable>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<PackageReference Include="BenchmarkDotNet" Version="0.14.0" />
12+
</ItemGroup>
13+
14+
<ItemGroup>
15+
<ProjectReference Include="..\Open.ChannelExtensions\Open.ChannelExtensions.csproj" />
16+
</ItemGroup>
17+
18+
</Project>
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
using Open.ChannelExtensions.Benchmarks;
2+
using BenchmarkDotNet.Running;
3+
BenchmarkRunner.Run<BatchDrain>();

0 commit comments

Comments
 (0)