Skip to content

Commit c031203

Browse files
committed
Add AnyOf vs Pattern benchmarks
1 parent c268ebf commit c031203

File tree

1 file changed

+144
-0
lines changed

1 file changed

+144
-0
lines changed
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
using BenchmarkDotNet.Attributes;
2+
using BenchmarkDotNet.Configs;
3+
using Parlot.Fluent;
4+
using System;
5+
using static Parlot.Fluent.Parsers;
6+
7+
namespace Parlot.Benchmarks;
8+
9+
[MemoryDiagnoser, GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory), ShortRunJob]
10+
public class AnyOfPatternBenchmarks
11+
{
12+
private const string DigitsSet = "0123456789";
13+
14+
// Explicit, non-contiguous set of non-digit chars used to build the non-digit inputs.
15+
// (No ranges; the input is generated by repeating this set.)
16+
private const string NonDigitsSet = "aekqzBHNPRTVXZ!$%&*+-/:;=?@[]_{}~";
17+
18+
private const string DigitsSmall = "28";
19+
private const string DigitsMedium = "975312468097";
20+
private const string DigitsLarge = "975312468097975312468097975312468097";
21+
22+
private const string NonDigitsSmall = "BH";
23+
private const string NonDigitsMedium = "aekqzBHNPRTV";
24+
private const string NonDigitsLarge = "aekqzB?NPRT~XZaekqzBHNPRTVXZa?kqzBH~PRTVXZ";
25+
26+
// 0=small, 1=medium, 2=large (large matches the previous sample)
27+
[Params(0, 1, 2)]
28+
public int Size { get; set; }
29+
30+
private string DigitsInput => Size switch
31+
{
32+
0 => DigitsSmall,
33+
1 => DigitsMedium,
34+
_ => DigitsLarge,
35+
};
36+
37+
private string LettersInput => Size switch
38+
{
39+
0 => NonDigitsSmall,
40+
1 => NonDigitsMedium,
41+
_ => NonDigitsLarge,
42+
};
43+
44+
private static readonly Parser<TextSpan> AnyOfDigits = Literals.AnyOf(DigitsSet);
45+
private static readonly Parser<TextSpan> PatternIsDigit = Literals.Pattern(char.IsDigit);
46+
private static readonly Parser<TextSpan> PatternDigitsContains = Literals.Pattern(static c => DigitsSet.Contains(c));
47+
48+
private static readonly Parser<TextSpan> AnyOfNonDigitsSet = Literals.AnyOf(NonDigitsSet);
49+
private static readonly Parser<TextSpan> PatternNonDigitsSetContains = Literals.Pattern(static c => NonDigitsSet.Contains(c));
50+
51+
private static readonly Parser<TextSpan> NoneOfDigits = Literals.NoneOf(DigitsSet);
52+
private static readonly Parser<TextSpan> PatternNotDigit = Literals.Pattern(static c => !char.IsDigit(c));
53+
private static readonly Parser<TextSpan> PatternNotDigitContains = Literals.Pattern(static c => !DigitsSet.Contains(c));
54+
55+
[GlobalSetup]
56+
public void Setup()
57+
{
58+
if (AnyOf_Digits().Length != DigitsInput.Length) throw new InvalidOperationException(nameof(AnyOf_Digits));
59+
if (Pattern_Digits().Length != DigitsInput.Length) throw new InvalidOperationException(nameof(Pattern_Digits));
60+
if (Pattern_Digits_Contains().Length != DigitsInput.Length) throw new InvalidOperationException(nameof(Pattern_Digits_Contains));
61+
62+
if (AnyOf_NonDigitsSet().Length != LettersInput.Length) throw new InvalidOperationException(nameof(AnyOf_NonDigitsSet));
63+
if (Pattern_NonDigitsSet_Contains().Length != LettersInput.Length) throw new InvalidOperationException(nameof(Pattern_NonDigitsSet_Contains));
64+
65+
if (NoneOf_NonDigits().Length != LettersInput.Length) throw new InvalidOperationException(nameof(NoneOf_NonDigits));
66+
if (Pattern_NonDigits().Length != LettersInput.Length) throw new InvalidOperationException(nameof(Pattern_NonDigits));
67+
if (Pattern_NonDigits_Contains().Length != LettersInput.Length) throw new InvalidOperationException(nameof(Pattern_NonDigits_Contains));
68+
}
69+
70+
[Benchmark(Baseline = true), BenchmarkCategory("Digits: AnyOf vs Pattern")]
71+
public TextSpan AnyOf_Digits() => AnyOfDigits.Parse(DigitsInput);
72+
73+
[Benchmark, BenchmarkCategory("Digits: AnyOf vs Pattern")]
74+
public TextSpan Pattern_Digits() => PatternIsDigit.Parse(DigitsInput);
75+
76+
[Benchmark, BenchmarkCategory("Digits: AnyOf vs Pattern")]
77+
public TextSpan Pattern_Digits_Contains() => PatternDigitsContains.Parse(DigitsInput);
78+
79+
[Benchmark(Baseline = true), BenchmarkCategory("NonDigitsSet: AnyOf vs Pattern")]
80+
public TextSpan AnyOf_NonDigitsSet() => AnyOfNonDigitsSet.Parse(LettersInput);
81+
82+
[Benchmark, BenchmarkCategory("NonDigitsSet: AnyOf vs Pattern")]
83+
public TextSpan Pattern_NonDigitsSet_Contains() => PatternNonDigitsSetContains.Parse(LettersInput);
84+
85+
[Benchmark(Baseline = true), BenchmarkCategory("NonDigits: NoneOf vs Pattern")]
86+
public TextSpan NoneOf_NonDigits() => NoneOfDigits.Parse(LettersInput);
87+
88+
[Benchmark, BenchmarkCategory("NonDigits: NoneOf vs Pattern")]
89+
public TextSpan Pattern_NonDigits() => PatternNotDigit.Parse(LettersInput);
90+
91+
[Benchmark, BenchmarkCategory("NonDigits: NoneOf vs Pattern")]
92+
public TextSpan Pattern_NonDigits_Contains() => PatternNotDigitContains.Parse(LettersInput);
93+
94+
}
95+
96+
97+
/*
98+
99+
CONCLUSION: AnyOf/NoneOf is faster than Pattern, with contiguous or non-contiguous sets.
100+
101+
BenchmarkDotNet v0.15.8, macOS Sequoia 15.7.3 (24G419) [Darwin 24.6.0]
102+
Apple M4 Pro, 1 CPU, 14 logical and 14 physical cores
103+
.NET SDK 10.0.100
104+
[Host] : .NET 10.0.0 (10.0.0, 10.0.25.52411), Arm64 RyuJIT armv8.0-a
105+
ShortRun : .NET 10.0.0 (10.0.0, 10.0.25.52411), Arm64 RyuJIT armv8.0-a
106+
107+
Job=ShortRun IterationCount=3 LaunchCount=1
108+
WarmupCount=3
109+
110+
| Method | Size | Mean | Error | StdDev | Ratio | Gen0 | Allocated | Alloc Ratio |
111+
|------------------------------ |----- |---------:|---------:|---------:|------:|-------:|----------:|------------:|
112+
| AnyOf_Digits | 0 | 18.05 ns | 1.340 ns | 0.073 ns | 1.00 | 0.0287 | 240 B | 1.00 |
113+
| Pattern_Digits | 0 | 21.35 ns | 1.299 ns | 0.071 ns | 1.18 | 0.0287 | 240 B | 1.00 |
114+
| Pattern_Digits_Contains | 0 | 21.10 ns | 3.389 ns | 0.186 ns | 1.17 | 0.0287 | 240 B | 1.00 |
115+
| | | | | | | | | |
116+
| AnyOf_Digits | 1 | 24.96 ns | 0.463 ns | 0.025 ns | 1.00 | 0.0287 | 240 B | 1.00 |
117+
| Pattern_Digits | 1 | 39.44 ns | 0.609 ns | 0.033 ns | 1.58 | 0.0287 | 240 B | 1.00 |
118+
| Pattern_Digits_Contains | 1 | 36.69 ns | 0.510 ns | 0.028 ns | 1.47 | 0.0287 | 240 B | 1.00 |
119+
| | | | | | | | | |
120+
| AnyOf_Digits | 2 | 39.51 ns | 0.946 ns | 0.052 ns | 1.00 | 0.0287 | 240 B | 1.00 |
121+
| Pattern_Digits | 2 | 83.82 ns | 0.864 ns | 0.047 ns | 2.12 | 0.0286 | 240 B | 1.00 |
122+
| Pattern_Digits_Contains | 2 | 75.88 ns | 1.194 ns | 0.065 ns | 1.92 | 0.0286 | 240 B | 1.00 |
123+
| | | | | | | | | |
124+
| NoneOf_NonDigits | 0 | 18.19 ns | 0.280 ns | 0.015 ns | 1.00 | 0.0287 | 240 B | 1.00 |
125+
| Pattern_NonDigits | 0 | 20.64 ns | 2.017 ns | 0.111 ns | 1.13 | 0.0287 | 240 B | 1.00 |
126+
| Pattern_NonDigits_Contains | 0 | 22.03 ns | 0.419 ns | 0.023 ns | 1.21 | 0.0287 | 240 B | 1.00 |
127+
| | | | | | | | | |
128+
| NoneOf_NonDigits | 1 | 25.03 ns | 1.219 ns | 0.067 ns | 1.00 | 0.0287 | 240 B | 1.00 |
129+
| Pattern_NonDigits | 1 | 34.61 ns | 1.571 ns | 0.086 ns | 1.38 | 0.0287 | 240 B | 1.00 |
130+
| Pattern_NonDigits_Contains | 1 | 36.86 ns | 1.482 ns | 0.081 ns | 1.47 | 0.0287 | 240 B | 1.00 |
131+
| | | | | | | | | |
132+
| NoneOf_NonDigits | 2 | 44.18 ns | 0.513 ns | 0.028 ns | 1.00 | 0.0287 | 240 B | 1.00 |
133+
| Pattern_NonDigits | 2 | 79.04 ns | 1.047 ns | 0.057 ns | 1.79 | 0.0286 | 240 B | 1.00 |
134+
| Pattern_NonDigits_Contains | 2 | 88.50 ns | 1.570 ns | 0.086 ns | 2.00 | 0.0286 | 240 B | 1.00 |
135+
| | | | | | | | | |
136+
| AnyOf_NonDigitsSet | 0 | 21.97 ns | 0.775 ns | 0.042 ns | 1.00 | 0.0287 | 240 B | 1.00 |
137+
| Pattern_NonDigitsSet_Contains | 0 | 21.40 ns | 0.591 ns | 0.032 ns | 0.97 | 0.0287 | 240 B | 1.00 |
138+
| | | | | | | | | |
139+
| AnyOf_NonDigitsSet | 1 | 29.08 ns | 0.313 ns | 0.017 ns | 1.00 | 0.0287 | 240 B | 1.00 |
140+
| Pattern_NonDigitsSet_Contains | 1 | 36.87 ns | 0.859 ns | 0.047 ns | 1.27 | 0.0287 | 240 B | 1.00 |
141+
| | | | | | | | | |
142+
| AnyOf_NonDigitsSet | 2 | 49.34 ns | 0.425 ns | 0.023 ns | 1.00 | 0.0287 | 240 B | 1.00 |
143+
| Pattern_NonDigitsSet_Contains | 2 | 88.78 ns | 0.838 ns | 0.046 ns | 1.80 | 0.0286 | 240 B | 1.00 |
144+
*/

0 commit comments

Comments
 (0)