1+ using BenchmarkDotNet . Attributes ;
2+ using BenchmarkDotNet . Columns ;
3+ using BenchmarkDotNet . Jobs ;
4+ using System . Collections . Generic ;
5+ using System . Runtime . CompilerServices ;
6+ using System . Runtime . InteropServices ;
7+ using static System . Net . Mime . MediaTypeNames ;
8+
9+ namespace Base58Encoding . Benchmarks ;
10+
11+ [ SimpleJob ( RuntimeMoniker . Net90 ) ]
12+ [ SimpleJob ( RuntimeMoniker . Net10_0 ) ]
13+ [ HideColumns ( "Job" , "Error" , "StdDev" , "Median" , "RatioSD" ) ]
14+ [ MemoryDiagnoser ]
15+ public class CountLeadingCharactersBenchmark
16+ {
17+ private static readonly string [ ] TestStrings = new [ ]
18+ {
19+ "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa" , // 0 leading zeros (standard P2PKH)
20+ "111111111111111111114oLvT2" , // Many leading zeros
21+ "1BoatSLRHtKNngkdXEeobR76b53LETtpyT" , // 0 leading zeros (standard)
22+
23+ "11111111111111111111111111111112" , // 31 leading zeros (system program)
24+ "1111111QLbz7JHiBTspS962RLKV8GndWFwiEaqKM" , // 1 leading zero
25+ "4TMFNY21gmHLT3HpPZDiXY6kQkGPpJsJRfisJ9T7rXdV" , // Typical Solana address (0 leading zeros)
26+ "So11111111111111111111111111111111111111112" , // Wrapped SOL (0 leading zeros)
27+
28+ "QmYwAPJzv5CZsnA625s3Xf2nemtYgPpHdWEz79ojWnPbdG" , // No leading zeros (typical)
29+ "11111118YbNzKyoNPP4TJLYwpB2B3NCvpNXJbRNcszU" , // Many leading zeros
30+ "3aDawfe6rm6QnFhJGppyyHVxXfEhBGYPMD8nzfLKNJqq" ,
31+ "4vJ9JU1bJJE96FWSJKvHsmmFADCg4gpZQff4P3bkLKi" , // 0 leading zeros
32+ "11BxRVsYgGSUZRSMaNcNjSzwMBYXqZQvBQr3BQznBFmpV" // 2 leading zeros
33+ } ;
34+
35+ [ Benchmark ( Description = "Simple Loop" ) ]
36+ public int SimpleLoop ( )
37+ {
38+ int totalCount = 0 ;
39+ foreach ( var str in TestStrings )
40+ {
41+ totalCount += CountLeadingCharactersSimple ( str , '1' ) ;
42+ }
43+ return totalCount ;
44+ }
45+
46+ [ Benchmark ( Baseline = true , Description = "Optimized sequential" ) ]
47+ public int Optimized ( )
48+ {
49+ int totalCount = 0 ;
50+ foreach ( var str in TestStrings )
51+ {
52+ totalCount += CountLeadingCharacters ( str , '1' ) ;
53+ }
54+ return totalCount ;
55+ }
56+
57+ [ Benchmark ( Description = "IndexOfAnyExcept" ) ]
58+ public int IndexOfAnyExcept ( )
59+ {
60+ int totalCount = 0 ;
61+ foreach ( var str in TestStrings )
62+ {
63+ totalCount += CountLeadingCharactersExcept ( str , '1' ) ;
64+ }
65+
66+ return totalCount ;
67+ }
68+
69+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
70+ public int CountLeadingCharactersExcept ( ReadOnlySpan < char > text , char target )
71+ {
72+ int mismatchIndex = text . IndexOfAnyExcept ( target ) ;
73+
74+ return mismatchIndex == - 1 ? text . Length : mismatchIndex ;
75+ }
76+
77+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
78+ private static int CountLeadingCharactersSimple ( ReadOnlySpan < char > text , char target )
79+ {
80+ int count = 0 ;
81+ for ( int i = 0 ; i < text . Length ; i ++ )
82+ {
83+ if ( text [ i ] != target )
84+ break ;
85+ count ++ ;
86+ }
87+ return count ;
88+ }
89+
90+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
91+ internal static int CountLeadingCharacters ( ReadOnlySpan < char > text , char target )
92+ {
93+ int count = 0 ;
94+ int length = text . Length ;
95+ ref char searchSpace = ref MemoryMarshal . GetReference ( text ) ;
96+ ulong targetPattern = ( ( ulong ) target ) | ( ( ( ulong ) target ) << 16 ) | ( ( ( ulong ) target ) << 32 ) | ( ( ( ulong ) target ) << 48 ) ;
97+
98+ while ( length >= 4 )
99+ {
100+ ulong fourChars = Unsafe . ReadUnaligned < ulong > ( ref Unsafe . As < char , byte > ( ref Unsafe . Add ( ref searchSpace , count ) ) ) ;
101+
102+ if ( fourChars != targetPattern )
103+ {
104+ if ( BitConverter . IsLittleEndian )
105+ {
106+ if ( ( fourChars & 0xFFFF ) != target ) return count ;
107+ if ( ( ( fourChars >> 16 ) & 0xFFFF ) != target ) return count + 1 ;
108+ if ( ( ( fourChars >> 32 ) & 0xFFFF ) != target ) return count + 2 ;
109+ return count + 3 ;
110+ }
111+ else
112+ {
113+ if ( ( ( fourChars >> 48 ) & 0xFFFF ) != target ) return count ;
114+ if ( ( ( fourChars >> 32 ) & 0xFFFF ) != target ) return count + 1 ;
115+ if ( ( ( fourChars >> 16 ) & 0xFFFF ) != target ) return count + 2 ;
116+ return count + 3 ;
117+ }
118+ }
119+
120+ count += 4 ;
121+ length -= 4 ;
122+ }
123+
124+ while ( count < text . Length && Unsafe . Add ( ref searchSpace , count ) == target )
125+ {
126+ count ++ ;
127+ }
128+
129+ return count ;
130+ }
131+ }
0 commit comments