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