Skip to content

Commit b424f8a

Browse files
feat: ZLinq integration for zero-allocation SplitAsSegments
- Add ZLinq 1.5.4 dependency for ValueEnumerable support - Implement StringSegmentSplitEnumerator with IValueEnumerator<StringSegment> - SplitAsSegments now returns ValueEnumerable<TEnumerator, T> - Zero heap allocations for foreach, Count(), Select(), Where() - Benchmarks confirm: 0 bytes allocated for pure iteration - 100% memory reduction vs BCL String.Split for foreach - 60% memory reduction for ToList() operations - Performance comparable to BCL String.Split (31.8ns vs 32.8ns)
1 parent 0817a05 commit b424f8a

13 files changed

Lines changed: 534 additions & 127 deletions
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
```
2+
3+
BenchmarkDotNet v0.15.8, Windows 10 (10.0.19045.6466/22H2/2022Update)
4+
AMD Ryzen 9 7900 3.70GHz, 1 CPU, 24 logical and 12 physical cores
5+
.NET SDK 10.0.102
6+
[Host] : .NET 8.0.23 (8.0.23, 8.0.2325.60607), X64 RyuJIT x86-64-v4
7+
Baseline : .NET 8.0.23 (8.0.23, 8.0.2325.60607), X64 RyuJIT x86-64-v4
8+
9+
Job=Baseline
10+
11+
```
12+
| Method | Mean | Error | StdDev | StdErr | Median | Min | Q1 | Q3 | Max | Op/s | Ratio | RatioSD | Gen0 | Gen1 | Allocated | Alloc Ratio |
13+
|------------------------------------- |-------------:|-----------:|-----------:|----------:|-------------:|-------------:|-------------:|-------------:|-------------:|-------------:|-------:|--------:|-------:|-------:|----------:|------------:|
14+
| &#39;Small - Foreach Only (no ToString)&#39; | 31.80 ns | 0.189 ns | 0.177 ns | 0.046 ns | 31.85 ns | 31.49 ns | 31.69 ns | 31.92 ns | 32.06 ns | 31,441,964.2 | 0.97 | 0.01 | - | - | - | 0.00 |
15+
| &#39;BCL String.Split (Reference)&#39; | 32.83 ns | 0.062 ns | 0.058 ns | 0.015 ns | 32.84 ns | 32.68 ns | 32.79 ns | 32.87 ns | 32.90 ns | 30,460,572.4 | 1.00 | 0.00 | 0.0119 | - | 200 B | 1.00 |
16+
| &#39;Small - Count()&#39; | 39.20 ns | 0.079 ns | 0.074 ns | 0.019 ns | 39.18 ns | 39.09 ns | 39.15 ns | 39.26 ns | 39.36 ns | 25,508,493.8 | 1.19 | 0.00 | - | - | - | 0.00 |
17+
| &#39;Small - Foreach with ToString&#39; | 42.27 ns | 0.132 ns | 0.123 ns | 0.032 ns | 42.27 ns | 42.04 ns | 42.22 ns | 42.35 ns | 42.51 ns | 23,657,927.6 | 1.29 | 0.00 | 0.0086 | - | 144 B | 0.72 |
18+
| &#39;Small - ToArray&#39; | 50.41 ns | 0.108 ns | 0.101 ns | 0.026 ns | 50.43 ns | 50.22 ns | 50.33 ns | 50.49 ns | 50.53 ns | 19,837,183.5 | 1.54 | 0.00 | 0.0052 | - | 88 B | 0.44 |
19+
| &#39;Medium - Foreach Only&#39; | 99.30 ns | 0.305 ns | 0.255 ns | 0.071 ns | 99.23 ns | 98.90 ns | 99.19 ns | 99.48 ns | 99.89 ns | 10,070,495.4 | 3.02 | 0.01 | - | - | - | 0.00 |
20+
| &#39;Medium - LINQ Select&#39; | 103.85 ns | 0.617 ns | 0.547 ns | 0.146 ns | 103.77 ns | 102.99 ns | 103.59 ns | 104.03 ns | 104.93 ns | 9,629,072.1 | 3.16 | 0.02 | - | - | - | 0.00 |
21+
| &#39;Medium - LINQ Where&#39; | 108.56 ns | 0.682 ns | 0.605 ns | 0.162 ns | 108.52 ns | 107.64 ns | 108.18 ns | 108.65 ns | 109.90 ns | 9,211,584.6 | 3.31 | 0.02 | - | - | - | 0.00 |
22+
| &#39;BCL Large String.Split&#39; | 6,669.08 ns | 130.648 ns | 128.314 ns | 32.078 ns | 6,646.10 ns | 6,446.95 ns | 6,613.36 ns | 6,746.26 ns | 6,935.43 ns | 149,945.7 | 203.14 | 3.81 | 2.3880 | 0.2975 | 39952 B | 199.76 |
23+
| &#39;Large (1000) - Foreach Only&#39; | 11,757.79 ns | 231.278 ns | 392.727 ns | 64.564 ns | 11,474.32 ns | 11,359.99 ns | 11,412.14 ns | 12,195.64 ns | 12,243.03 ns | 85,050.0 | 358.15 | 11.83 | - | - | - | 0.00 |
24+
| &#39;Large (1000) - ToList&#39; | 12,432.33 ns | 13.625 ns | 12.745 ns | 3.291 ns | 12,433.69 ns | 12,405.13 ns | 12,426.04 ns | 12,439.79 ns | 12,456.84 ns | 80,435.5 | 378.70 | 0.75 | 0.9460 | 0.0458 | 16056 B | 80.28 |
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
Method,Job,AnalyzeLaunchVariance,EvaluateOverhead,MaxAbsoluteError,MaxRelativeError,MinInvokeCount,MinIterationTime,OutlierMode,Affinity,EnvironmentVariables,Jit,LargeAddressAware,Platform,PowerPlanMode,Runtime,AllowVeryLargeObjects,Concurrent,CpuGroups,Force,HeapAffinitizeMask,HeapCount,NoAffinitize,RetainVm,Server,Arguments,BuildConfiguration,Clock,EngineFactory,NuGetReferences,Toolchain,IsMutator,InvocationCount,IterationCount,IterationTime,LaunchCount,MaxIterationCount,MaxWarmupIterationCount,MemoryRandomization,MinIterationCount,MinWarmupIterationCount,RunStrategy,UnrollFactor,WarmupCount,Mean,Error,StdDev,StdErr,Median,Min,Q1,Q3,Max,Op/s,Ratio,RatioSD,Gen0,Gen1,Allocated,Alloc Ratio
2+
'Small - Foreach Only (no ToString)',Baseline,False,Default,Default,Default,Default,Default,Default,111111111111111111111111,Empty,RyuJit,Default,X64,8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c,.NET 8.0,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,16,Default,31.80 ns,0.189 ns,0.177 ns,0.046 ns,31.85 ns,31.49 ns,31.69 ns,31.92 ns,32.06 ns,"31,441,964.2",0.97,0.01,0.0000,0.0000,0 B,0.00
3+
'BCL String.Split (Reference)',Baseline,False,Default,Default,Default,Default,Default,Default,111111111111111111111111,Empty,RyuJit,Default,X64,8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c,.NET 8.0,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,16,Default,32.83 ns,0.062 ns,0.058 ns,0.015 ns,32.84 ns,32.68 ns,32.79 ns,32.87 ns,32.90 ns,"30,460,572.4",1.00,0.00,0.0119,0.0000,200 B,1.00
4+
'Small - Count()',Baseline,False,Default,Default,Default,Default,Default,Default,111111111111111111111111,Empty,RyuJit,Default,X64,8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c,.NET 8.0,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,16,Default,39.20 ns,0.079 ns,0.074 ns,0.019 ns,39.18 ns,39.09 ns,39.15 ns,39.26 ns,39.36 ns,"25,508,493.8",1.19,0.00,0.0000,0.0000,0 B,0.00
5+
'Small - Foreach with ToString',Baseline,False,Default,Default,Default,Default,Default,Default,111111111111111111111111,Empty,RyuJit,Default,X64,8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c,.NET 8.0,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,16,Default,42.27 ns,0.132 ns,0.123 ns,0.032 ns,42.27 ns,42.04 ns,42.22 ns,42.35 ns,42.51 ns,"23,657,927.6",1.29,0.00,0.0086,0.0000,144 B,0.72
6+
'Small - ToArray',Baseline,False,Default,Default,Default,Default,Default,Default,111111111111111111111111,Empty,RyuJit,Default,X64,8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c,.NET 8.0,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,16,Default,50.41 ns,0.108 ns,0.101 ns,0.026 ns,50.43 ns,50.22 ns,50.33 ns,50.49 ns,50.53 ns,"19,837,183.5",1.54,0.00,0.0052,0.0000,88 B,0.44
7+
'Medium - Foreach Only',Baseline,False,Default,Default,Default,Default,Default,Default,111111111111111111111111,Empty,RyuJit,Default,X64,8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c,.NET 8.0,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,16,Default,99.30 ns,0.305 ns,0.255 ns,0.071 ns,99.23 ns,98.90 ns,99.19 ns,99.48 ns,99.89 ns,"10,070,495.4",3.02,0.01,0.0000,0.0000,0 B,0.00
8+
'Medium - LINQ Select',Baseline,False,Default,Default,Default,Default,Default,Default,111111111111111111111111,Empty,RyuJit,Default,X64,8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c,.NET 8.0,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,16,Default,103.85 ns,0.617 ns,0.547 ns,0.146 ns,103.77 ns,102.99 ns,103.59 ns,104.03 ns,104.93 ns,"9,629,072.1",3.16,0.02,0.0000,0.0000,0 B,0.00
9+
'Medium - LINQ Where',Baseline,False,Default,Default,Default,Default,Default,Default,111111111111111111111111,Empty,RyuJit,Default,X64,8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c,.NET 8.0,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,16,Default,108.56 ns,0.682 ns,0.605 ns,0.162 ns,108.52 ns,107.64 ns,108.18 ns,108.65 ns,109.90 ns,"9,211,584.6",3.31,0.02,0.0000,0.0000,0 B,0.00
10+
'BCL Large String.Split',Baseline,False,Default,Default,Default,Default,Default,Default,111111111111111111111111,Empty,RyuJit,Default,X64,8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c,.NET 8.0,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,16,Default,"6,669.08 ns",130.648 ns,128.314 ns,32.078 ns,"6,646.10 ns","6,446.95 ns","6,613.36 ns","6,746.26 ns","6,935.43 ns","149,945.7",203.14,3.81,2.3880,0.2975,39952 B,199.76
11+
'Large (1000) - Foreach Only',Baseline,False,Default,Default,Default,Default,Default,Default,111111111111111111111111,Empty,RyuJit,Default,X64,8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c,.NET 8.0,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,16,Default,"11,757.79 ns",231.278 ns,392.727 ns,64.564 ns,"11,474.32 ns","11,359.99 ns","11,412.14 ns","12,195.64 ns","12,243.03 ns","85,050.0",358.15,11.83,0.0000,0.0000,0 B,0.00
12+
'Large (1000) - ToList',Baseline,False,Default,Default,Default,Default,Default,Default,111111111111111111111111,Empty,RyuJit,Default,X64,8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c,.NET 8.0,False,True,False,True,Default,Default,False,False,False,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,Default,16,Default,"12,432.33 ns",13.625 ns,12.745 ns,3.291 ns,"12,433.69 ns","12,405.13 ns","12,426.04 ns","12,439.79 ns","12,456.84 ns","80,435.5",378.70,0.75,0.9460,0.0458,16056 B,80.28
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<!DOCTYPE html>
2+
<html lang='en'>
3+
<head>
4+
<meta charset='utf-8' />
5+
<title>Open.Text.Benchmarks.SplitAsSegmentsBaselineBenchmark-20260131-105539</title>
6+
7+
<style type="text/css">
8+
table { border-collapse: collapse; display: block; width: 100%; overflow: auto; }
9+
td, th { padding: 6px 13px; border: 1px solid #ddd; text-align: right; }
10+
tr { background-color: #fff; border-top: 1px solid #ccc; }
11+
tr:nth-child(even) { background: #f8f8f8; }
12+
</style>
13+
</head>
14+
<body>
15+
<pre><code>
16+
BenchmarkDotNet v0.15.8, Windows 10 (10.0.19045.6466/22H2/2022Update)
17+
AMD Ryzen 9 7900 3.70GHz, 1 CPU, 24 logical and 12 physical cores
18+
.NET SDK 10.0.102
19+
[Host] : .NET 8.0.23 (8.0.23, 8.0.2325.60607), X64 RyuJIT x86-64-v4
20+
Baseline : .NET 8.0.23 (8.0.23, 8.0.2325.60607), X64 RyuJIT x86-64-v4
21+
</code></pre>
22+
<pre><code>Job=Baseline
23+
</code></pre>
24+
25+
<table>
26+
<thead><tr><th>Method </th><th>Mean </th><th>Error</th><th>StdDev</th><th>StdErr</th><th>Median</th><th>Min </th><th>Q1 </th><th>Q3 </th><th>Max </th><th>Op/s </th><th>Ratio</th><th>RatioSD</th><th>Gen0</th><th>Gen1</th><th>Allocated</th><th>Alloc Ratio</th>
27+
</tr>
28+
</thead><tbody><tr><td>&#39;Small - Foreach Only (no ToString)&#39;</td><td>31.80 ns</td><td>0.189 ns</td><td>0.177 ns</td><td>0.046 ns</td><td>31.85 ns</td><td>31.49 ns</td><td>31.69 ns</td><td>31.92 ns</td><td>32.06 ns</td><td>31,441,964.2</td><td>0.97</td><td>0.01</td><td>-</td><td>-</td><td>-</td><td>0.00</td>
29+
</tr><tr><td>&#39;BCL String.Split (Reference)&#39;</td><td>32.83 ns</td><td>0.062 ns</td><td>0.058 ns</td><td>0.015 ns</td><td>32.84 ns</td><td>32.68 ns</td><td>32.79 ns</td><td>32.87 ns</td><td>32.90 ns</td><td>30,460,572.4</td><td>1.00</td><td>0.00</td><td>0.0119</td><td>-</td><td>200 B</td><td>1.00</td>
30+
</tr><tr><td>&#39;Small - Count()&#39;</td><td>39.20 ns</td><td>0.079 ns</td><td>0.074 ns</td><td>0.019 ns</td><td>39.18 ns</td><td>39.09 ns</td><td>39.15 ns</td><td>39.26 ns</td><td>39.36 ns</td><td>25,508,493.8</td><td>1.19</td><td>0.00</td><td>-</td><td>-</td><td>-</td><td>0.00</td>
31+
</tr><tr><td>&#39;Small - Foreach with ToString&#39;</td><td>42.27 ns</td><td>0.132 ns</td><td>0.123 ns</td><td>0.032 ns</td><td>42.27 ns</td><td>42.04 ns</td><td>42.22 ns</td><td>42.35 ns</td><td>42.51 ns</td><td>23,657,927.6</td><td>1.29</td><td>0.00</td><td>0.0086</td><td>-</td><td>144 B</td><td>0.72</td>
32+
</tr><tr><td>&#39;Small - ToArray&#39;</td><td>50.41 ns</td><td>0.108 ns</td><td>0.101 ns</td><td>0.026 ns</td><td>50.43 ns</td><td>50.22 ns</td><td>50.33 ns</td><td>50.49 ns</td><td>50.53 ns</td><td>19,837,183.5</td><td>1.54</td><td>0.00</td><td>0.0052</td><td>-</td><td>88 B</td><td>0.44</td>
33+
</tr><tr><td>&#39;Medium - Foreach Only&#39;</td><td>99.30 ns</td><td>0.305 ns</td><td>0.255 ns</td><td>0.071 ns</td><td>99.23 ns</td><td>98.90 ns</td><td>99.19 ns</td><td>99.48 ns</td><td>99.89 ns</td><td>10,070,495.4</td><td>3.02</td><td>0.01</td><td>-</td><td>-</td><td>-</td><td>0.00</td>
34+
</tr><tr><td>&#39;Medium - LINQ Select&#39;</td><td>103.85 ns</td><td>0.617 ns</td><td>0.547 ns</td><td>0.146 ns</td><td>103.77 ns</td><td>102.99 ns</td><td>103.59 ns</td><td>104.03 ns</td><td>104.93 ns</td><td>9,629,072.1</td><td>3.16</td><td>0.02</td><td>-</td><td>-</td><td>-</td><td>0.00</td>
35+
</tr><tr><td>&#39;Medium - LINQ Where&#39;</td><td>108.56 ns</td><td>0.682 ns</td><td>0.605 ns</td><td>0.162 ns</td><td>108.52 ns</td><td>107.64 ns</td><td>108.18 ns</td><td>108.65 ns</td><td>109.90 ns</td><td>9,211,584.6</td><td>3.31</td><td>0.02</td><td>-</td><td>-</td><td>-</td><td>0.00</td>
36+
</tr><tr><td>&#39;BCL Large String.Split&#39;</td><td>6,669.08 ns</td><td>130.648 ns</td><td>128.314 ns</td><td>32.078 ns</td><td>6,646.10 ns</td><td>6,446.95 ns</td><td>6,613.36 ns</td><td>6,746.26 ns</td><td>6,935.43 ns</td><td>149,945.7</td><td>203.14</td><td>3.81</td><td>2.3880</td><td>0.2975</td><td>39952 B</td><td>199.76</td>
37+
</tr><tr><td>&#39;Large (1000) - Foreach Only&#39;</td><td>11,757.79 ns</td><td>231.278 ns</td><td>392.727 ns</td><td>64.564 ns</td><td>11,474.32 ns</td><td>11,359.99 ns</td><td>11,412.14 ns</td><td>12,195.64 ns</td><td>12,243.03 ns</td><td>85,050.0</td><td>358.15</td><td>11.83</td><td>-</td><td>-</td><td>-</td><td>0.00</td>
38+
</tr><tr><td>&#39;Large (1000) - ToList&#39;</td><td>12,432.33 ns</td><td>13.625 ns</td><td>12.745 ns</td><td>3.291 ns</td><td>12,433.69 ns</td><td>12,405.13 ns</td><td>12,426.04 ns</td><td>12,439.79 ns</td><td>12,456.84 ns</td><td>80,435.5</td><td>378.70</td><td>0.75</td><td>0.9460</td><td>0.0458</td><td>16056 B</td><td>80.28</td>
39+
</tr></tbody></table>
40+
</body>
41+
</html>
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
using BenchmarkDotNet.Attributes;
2+
using BenchmarkDotNet.Columns;
3+
using BenchmarkDotNet.Configs;
4+
using BenchmarkDotNet.Diagnosers;
5+
using BenchmarkDotNet.Jobs;
6+
using BenchmarkDotNet.Order;
7+
using Open.Text;
8+
using ZLinq;
9+
10+
namespace Open.Text.Benchmarks;
11+
12+
/// <summary>
13+
/// Focused benchmark for SplitAsSegments to establish baseline allocation behavior.
14+
/// Goal: Prove current implementation allocates and measure by how much.
15+
/// </summary>
16+
[Config(typeof(Config))]
17+
[MemoryDiagnoser]
18+
[Orderer(SummaryOrderPolicy.FastestToSlowest)]
19+
public class SplitAsSegmentsBaselineBenchmark
20+
{
21+
private class Config : ManualConfig
22+
{
23+
public Config()
24+
{
25+
AddDiagnoser(MemoryDiagnoser.Default);
26+
AddColumn(StatisticColumn.AllStatistics);
27+
AddJob(Job.Default.WithId("Baseline"));
28+
}
29+
}
30+
31+
private const string SmallString = "apple,banana,cherry,date";
32+
private const string MediumString = "apple,banana,cherry,date,elderberry,fig,grape,honeydew,kiwi,lemon";
33+
private readonly string _largeString = string.Join(",", Enumerable.Range(1, 1000));
34+
35+
// ========== Small String Tests ==========
36+
37+
[Benchmark(Description = "Small - Foreach Only (no ToString)")]
38+
public int Small_ForeachOnly()
39+
{
40+
int count = 0;
41+
foreach (var segment in SmallString.AsSegment().SplitAsSegments(','))
42+
{
43+
count += segment.Length;
44+
}
45+
return count;
46+
}
47+
48+
[Benchmark(Description = "Small - Foreach with ToString")]
49+
public int Small_ForeachWithToString()
50+
{
51+
int totalLen = 0;
52+
foreach (var segment in SmallString.AsSegment().SplitAsSegments(','))
53+
{
54+
var str = segment.ToString();
55+
totalLen += str.Length;
56+
}
57+
return totalLen;
58+
}
59+
60+
[Benchmark(Description = "Small - ToArray")]
61+
public int Small_ToArray()
62+
{
63+
var arr = SmallString.AsSegment().SplitAsSegments(',').ToArray();
64+
return arr.Length;
65+
}
66+
67+
[Benchmark(Description = "Small - Count()")]
68+
public int Small_Count()
69+
{
70+
return SmallString.AsSegment().SplitAsSegments(',').Count();
71+
}
72+
73+
// ========== Medium String Tests ==========
74+
75+
[Benchmark(Description = "Medium - Foreach Only")]
76+
public int Medium_ForeachOnly()
77+
{
78+
int count = 0;
79+
foreach (var segment in MediumString.AsSegment().SplitAsSegments(','))
80+
{
81+
count += segment.Length;
82+
}
83+
return count;
84+
}
85+
86+
[Benchmark(Description = "Medium - LINQ Where")]
87+
public int Medium_LinqWhere()
88+
{
89+
return MediumString.AsSegment()
90+
.SplitAsSegments(',')
91+
.Where(s => s.Length > 5)
92+
.Count();
93+
}
94+
95+
[Benchmark(Description = "Medium - LINQ Select")]
96+
public int Medium_LinqSelect()
97+
{
98+
return MediumString.AsSegment()
99+
.SplitAsSegments(',')
100+
.Select(s => s.Length)
101+
.Sum();
102+
}
103+
104+
// ========== Large String Tests ==========
105+
106+
[Benchmark(Description = "Large (1000) - Foreach Only")]
107+
public int Large_ForeachOnly()
108+
{
109+
int count = 0;
110+
foreach (var segment in _largeString.AsSegment().SplitAsSegments(','))
111+
{
112+
count++;
113+
}
114+
return count;
115+
}
116+
117+
[Benchmark(Description = "Large (1000) - ToList")]
118+
public int Large_ToList()
119+
{
120+
var list = _largeString.AsSegment().SplitAsSegments(',').ToList();
121+
return list.Count;
122+
}
123+
124+
// ========== BCL Comparison (Baseline) ==========
125+
126+
[Benchmark(Baseline = true, Description = "BCL String.Split (Reference)")]
127+
public int BclStringSplit()
128+
{
129+
var arr = SmallString.Split(',');
130+
return arr.Length;
131+
}
132+
133+
[Benchmark(Description = "BCL Large String.Split")]
134+
public int BclStringSplit_Large()
135+
{
136+
var arr = _largeString.Split(',');
137+
return arr.Length;
138+
}
139+
}

Benchmarks/Open.Text.Benchmarks.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
<ItemGroup>
1313
<PackageReference Include="BenchmarkDotNet" Version="0.15.8" />
1414
<PackageReference Include="FastEnum" Version="2.0.6" />
15+
<PackageReference Include="ZLinq" Version="1.5.4" />
1516
</ItemGroup>
1617

1718
<ItemGroup>

Benchmarks/Program.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
using BenchmarkDotNet.Running;
22
using Open.Text.Benchmarks;
33

4+
// Quick allocation test if --quicktest argument provided
5+
if (args.Length > 0 && args.Contains("--quicktest"))
6+
{
7+
SplitAsSegmentsQuickTest.Run();
8+
return;
9+
}
10+
411
//BenchmarkSwitcher
512
// .FromAssembly(typeof(Program).Assembly)
613
// .Run(args); // crucial to make it work
@@ -11,4 +18,4 @@
1118
//BenchmarkRunner.Run<EnumAttributeTests>();
1219
//BenchmarkRunner.Run<IsDefinedTests>();
1320
//BenchmarkRunner.Run<StringConcatTests>();
14-
BenchmarkRunner.Run<IndexOfTests>();
21+
BenchmarkRunner.Run<SplitAsSegmentsBaselineBenchmark>();

0 commit comments

Comments
 (0)