-
-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathExperimentalAlgorithmsBenchmarks.cs
More file actions
209 lines (185 loc) · 8.9 KB
/
ExperimentalAlgorithmsBenchmarks.cs
File metadata and controls
209 lines (185 loc) · 8.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
using System.Collections.Generic;
using System.Linq;
using BenchmarkDotNet.Attributes;
using Platform.Collections.Arrays;
using Platform.Collections.Lists;
using Platform.Converters;
using Platform.Data.Doublets.Memory.United.Generic;
using Platform.Memory;
using TLinkAddress = System.UInt64;
#pragma warning disable CA1822 // Mark members as static
namespace Platform.Data.Doublets.Benchmarks
{
/// <summary>
/// Benchmarks comparing experimental versions of similar algorithms.
/// This demonstrates different approaches to link data collection and processing,
/// showing performance differences between various implementation strategies.
/// </summary>
[SimpleJob]
[MemoryDiagnoser]
public class ExperimentalAlgorithmsBenchmarks
{
private static ILinks<TLinkAddress> _links;
private static TLinkAddress _any;
private static HeapResizableDirectMemory _dataMemory;
[Params(100, 1000, 5000)]
public static int N;
[GlobalSetup]
public static void Setup()
{
_dataMemory = new HeapResizableDirectMemory();
_links = new UnitedMemoryLinks<TLinkAddress>(_dataMemory).DecorateWithAutomaticUniquenessAndUsagesResolution();
_any = _links.Constants.Any;
var firstLink = _links.CreatePoint();
// Create test data
for (int i = 0; i < N; i++)
{
var link = _links.Create();
_links.Update(link, firstLink, link);
}
for (int i = 0; i < N; i++)
{
var link = _links.Create();
_links.Update(link, link, firstLink);
}
}
[GlobalCleanup]
public static void Cleanup()
{
_dataMemory.Dispose();
}
/// <summary>
/// Version 1: Using pre-allocated array with exact size calculation.
/// This is the most memory-efficient approach but requires two passes.
/// </summary>
[Benchmark]
public IList<IList<TLinkAddress>?> LinkCollection_V1_PreallocatedArray()
{
var addressToInt64Converter = CheckedConverter<TLinkAddress, long>.Default;
var usagesAsSourceQuery = new Link<TLinkAddress>(_any, 1UL, _any);
var usagesAsSourceCount = addressToInt64Converter.Convert(_links.Count(usagesAsSourceQuery));
var usagesAsTargetQuery = new Link<TLinkAddress>(_any, _any, 1UL);
var usagesAsTargetCount = addressToInt64Converter.Convert(_links.Count(usagesAsTargetQuery));
var totalUsages = usagesAsSourceCount + usagesAsTargetCount;
var usages = new IList<TLinkAddress>?[totalUsages];
var usagesFiller = new ArrayFiller<IList<TLinkAddress>?, TLinkAddress>(usages, _links.Constants.Continue);
_links.Each(usagesFiller.AddAndReturnConstant, usagesAsSourceQuery);
_links.Each(usagesFiller.AddAndReturnConstant, usagesAsTargetQuery);
return usages;
}
/// <summary>
/// Version 2: Using dynamic List without pre-calculation.
/// This is simpler but less memory-efficient due to dynamic resizing.
/// </summary>
[Benchmark]
public IList<IList<TLinkAddress>?> LinkCollection_V2_DynamicList()
{
var usagesAsSourceQuery = new Link<TLinkAddress>(_any, 1UL, _any);
var usagesAsTargetQuery = new Link<TLinkAddress>(_any, _any, 1UL);
var usages = new List<IList<TLinkAddress>?>();
var usagesFiller = new ListFiller<IList<TLinkAddress>?, TLinkAddress>(usages, _links.Constants.Continue);
_links.Each(usagesFiller.AddAndReturnConstant, usagesAsSourceQuery);
_links.Each(usagesFiller.AddAndReturnConstant, usagesAsTargetQuery);
return usages;
}
/// <summary>
/// Version 3: Using List with pre-calculated capacity.
/// This combines the benefits of both approaches - single allocation with dynamic structure.
/// </summary>
[Benchmark]
public IList<IList<TLinkAddress>?> LinkCollection_V3_PreallocatedList()
{
var addressToInt64Converter = CheckedConverter<TLinkAddress, long>.Default;
var usagesAsSourceQuery = new Link<TLinkAddress>(_any, 1UL, _any);
var usagesAsSourceCount = addressToInt64Converter.Convert(_links.Count(usagesAsSourceQuery));
var usagesAsTargetQuery = new Link<TLinkAddress>(_any, _any, 1UL);
var usagesAsTargetCount = addressToInt64Converter.Convert(_links.Count(usagesAsTargetQuery));
var totalUsages = usagesAsSourceCount + usagesAsTargetCount;
var usages = new List<IList<TLinkAddress>?>((int)totalUsages);
var usagesFiller = new ListFiller<IList<TLinkAddress>?, TLinkAddress>(usages, _links.Constants.Continue);
_links.Each(usagesFiller.AddAndReturnConstant, usagesAsSourceQuery);
_links.Each(usagesFiller.AddAndReturnConstant, usagesAsTargetQuery);
return usages;
}
/// <summary>
/// Version 4: Using LINQ-based approach for collection.
/// This is the most readable but potentially least performant approach.
/// </summary>
[Benchmark]
public IList<IList<TLinkAddress>?> LinkCollection_V4_LinqBased()
{
var usagesAsSourceQuery = new Link<TLinkAddress>(_any, 1UL, _any);
var usagesAsTargetQuery = new Link<TLinkAddress>(_any, _any, 1UL);
var sourceUsages = new List<IList<TLinkAddress>?>();
var targetUsages = new List<IList<TLinkAddress>?>();
_links.Each(link => { sourceUsages.Add(link); return _links.Constants.Continue; }, usagesAsSourceQuery);
_links.Each(link => { targetUsages.Add(link); return _links.Constants.Continue; }, usagesAsTargetQuery);
return sourceUsages.Concat(targetUsages).ToList();
}
/// <summary>
/// Version 5: Experimental batch processing approach.
/// Processes links in batches to potentially improve cache locality.
/// </summary>
[Benchmark]
public IList<IList<TLinkAddress>?> LinkCollection_V5_BatchProcessing()
{
const int batchSize = 100;
var usagesAsSourceQuery = new Link<TLinkAddress>(_any, 1UL, _any);
var usagesAsTargetQuery = new Link<TLinkAddress>(_any, _any, 1UL);
var usages = new List<IList<TLinkAddress>?>();
// Process in batches
var batch = new List<IList<TLinkAddress>?>(batchSize);
_links.Each(link =>
{
batch.Add(link);
if (batch.Count >= batchSize)
{
usages.AddRange(batch);
batch.Clear();
}
return _links.Constants.Continue;
}, usagesAsSourceQuery);
if (batch.Count > 0)
{
usages.AddRange(batch);
batch.Clear();
}
_links.Each(link =>
{
batch.Add(link);
if (batch.Count >= batchSize)
{
usages.AddRange(batch);
batch.Clear();
}
return _links.Constants.Continue;
}, usagesAsTargetQuery);
if (batch.Count > 0)
{
usages.AddRange(batch);
}
return usages;
}
/// <summary>
/// Version 6: Memory-optimized approach using ArrayPool.
/// Uses pooled arrays to reduce GC pressure for temporary storage.
/// </summary>
[Benchmark]
public IList<IList<TLinkAddress>?> LinkCollection_V6_PooledArrays()
{
var addressToInt64Converter = CheckedConverter<TLinkAddress, long>.Default;
var usagesAsSourceQuery = new Link<TLinkAddress>(_any, 1UL, _any);
var usagesAsSourceCount = addressToInt64Converter.Convert(_links.Count(usagesAsSourceQuery));
var usagesAsTargetQuery = new Link<TLinkAddress>(_any, _any, 1UL);
var usagesAsTargetCount = addressToInt64Converter.Convert(_links.Count(usagesAsTargetQuery));
var totalUsages = usagesAsSourceCount + usagesAsTargetCount;
// Use a simple approach since ArrayPool might not be available
var usages = new List<IList<TLinkAddress>?>((int)totalUsages);
var tempList = new List<IList<TLinkAddress>?>();
_links.Each(link => { tempList.Add(link); return _links.Constants.Continue; }, usagesAsSourceQuery);
_links.Each(link => { tempList.Add(link); return _links.Constants.Continue; }, usagesAsTargetQuery);
usages.AddRange(tempList);
return usages;
}
}
}