Home Benchmark regex
Post
Cancel

Benchmark regex

Regular expressions (regex) are patterns used to match character combinations in strings. In .NET, there are several ways to use regex for matching. The simplest form is:

1
System.Text.RegularExpressions.Regex.IsMatch("my text", "[a]");

It’s important to note that using Regex.IsMatch(input, pattern) directly is not performant and should generally be avoided. A slightly better approach is to create an instance of the Regex class with the pattern:

1
2
var pattern = new Regex("[a]");
pattern.IsMatch("my text");

This approach caches the pattern, but it can still be slow for repeated use. To improve performance, you can compile the pattern:

1
2
var pattern = new Regex("[a]", RegexOptions.Compiled);
pattern.IsMatch("my text");

With RegexOptions.Compiled, the compiler generates dynamic code to speed up matching. This reduces the overhead of interpreting the regex pattern at runtime, resulting in faster execution, especially for patterns used frequently.

For a long time, this was the fastest way to use regular expressions in .NET. However, since .NET 7, a fourth option was introduced: source-generated regex using a source generator. You can use it as follows:

1
2
3
4
5
6
7
8
9
10
11
partial class MyClass
{
    public static void MyMethod()
    {
        var pattern = MyRegex();
        pattern.IsMatch("my text");
    }

    [GeneratedRegex("[a]", RegexOptions.Compiled)]
    private static partial Regex MyRegex();
}

The generated code can be found under Dependencies → Analyzers → System.Text.RegularExpressions. The source generator essentially creates the same code as the compiled approach, but at compile time, so there is no runtime overhead. This method also benefits ahead-of-time (AOT) compilation in .NET 10 and later, since it avoids generating dynamic code at runtime.

The benchmark

The benchmarks were created using BenchmarkDotNet (version 0.15.8).

Benchmark code

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
using BenchmarkDotNet.Attributes;
using System.Text.RegularExpressions;

_ = BenchmarkDotNet.Running.BenchmarkRunner.Run<RegexBenchmark>();

public partial class RegexBenchmark
{
    [Params(
        "first.lastname@outlook.com"
        , "invalid-email"
        , "another.invalid-email@"
        , "test@"
        , "user@domain"
        , "a@b.c"
        , "very.long.email.address.with.many.dots@subdomain.example.com"
        )]
    public string input;

    private readonly string pattern = "^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$";
    private readonly Regex regex = new("^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$");
    private readonly Regex regexCompiled = new("^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$", RegexOptions.Compiled);

    [Benchmark]
    public bool RegexWithStringPattern() => Regex.IsMatch(input, pattern);

    [Benchmark]
    public bool RegexWithNewRegexString() => regex.IsMatch(input);

    [Benchmark(Baseline = true)]
    public bool RegexWithNewRegexStringCompiled() => regexCompiled.IsMatch(input);

    [Benchmark]
    public bool RegexWithStaticPartialMethod() => EmailPartialRegex().IsMatch(input);

    [GeneratedRegex("^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$")]
    private static partial Regex EmailPartialRegex();
}

Overall benchmark results

MethodinputMeanErrorStdDevRatioRatioSD
RegexWithStringPatterna@b.c142.86 ns1.514 ns1.416 ns3.100.04
RegexWithNewRegexStringa@b.c134.36 ns0.647 ns0.505 ns2.910.02
RegexWithNewRegexStringCompileda@b.c46.14 ns0.381 ns0.357 ns1.000.01
RegexWithStaticPartialMethoda@b.c43.44 ns0.318 ns0.282 ns0.940.01
       
RegexWithStringPatternanoth(…)mail@ [22]95.44 ns0.654 ns0.511 ns3.120.06
RegexWithNewRegexStringanoth(…)mail@ [22]95.70 ns0.518 ns0.459 ns3.130.06
RegexWithNewRegexStringCompiledanoth(…)mail@ [22]30.60 ns0.623 ns0.583 ns1.000.03
RegexWithStaticPartialMethodanoth(…)mail@ [22]30.02 ns0.152 ns0.142 ns0.980.02
       
RegexWithStringPatternfirst(…)k.com [26]196.91 ns2.896 ns2.418 ns2.960.04
RegexWithNewRegexStringfirst(…)k.com [26]191.30 ns2.154 ns2.015 ns2.880.03
RegexWithNewRegexStringCompiledfirst(…)k.com [26]66.47 ns0.463 ns0.411 ns1.000.01
RegexWithStaticPartialMethodfirst(…)k.com [26]64.68 ns0.502 ns0.470 ns0.970.01
       
RegexWithStringPatterninvalid-email71.57 ns0.490 ns0.434 ns2.620.02
RegexWithNewRegexStringinvalid-email69.20 ns0.600 ns0.501 ns2.530.02
RegexWithNewRegexStringCompiledinvalid-email27.32 ns0.182 ns0.161 ns1.000.01
RegexWithStaticPartialMethodinvalid-email26.40 ns0.080 ns0.071 ns0.970.01
       
RegexWithStringPatterntest@65.96 ns0.679 ns0.567 ns2.600.02
RegexWithNewRegexStringtest@57.34 ns0.635 ns0.594 ns2.260.02
RegexWithNewRegexStringCompiledtest@25.39 ns0.091 ns0.081 ns1.000.00
RegexWithStaticPartialMethodtest@24.95 ns0.157 ns0.146 ns0.980.01
       
RegexWithStringPatternuser@domain119.30 ns0.742 ns0.658 ns2.270.02
RegexWithNewRegexStringuser@domain115.68 ns1.357 ns1.269 ns2.200.03
RegexWithNewRegexStringCompileduser@domain52.62 ns0.322 ns0.301 ns1.000.01
RegexWithStaticPartialMethoduser@domain45.81 ns0.315 ns0.263 ns0.870.01
       
RegexWithStringPatternvery.(…)e.com [60]312.25 ns2.419 ns2.145 ns3.440.03
RegexWithNewRegexStringvery.(…)e.com [60]300.72 ns2.023 ns1.793 ns3.310.03
RegexWithNewRegexStringCompiledvery.(…)e.com [60]90.85 ns0.506 ns0.474 ns1.000.01
RegexWithStaticPartialMethodvery.(…)e.com [60]86.00 ns0.726 ns0.679 ns0.950.01

Conclusion: Source-generated regex ([GeneratedRegex]) is consistently 3–13% faster than compiled regex and up to 3.4× faster than creating regex instances from string patterns, making it the clear winner for modern .NET applications.

Results for cold start (JIT impact)

For these results, update the code to use only the two fastest options for better visibility:

1
[SimpleJob(runStrategy:RunStrategy.ColdStart)]

This uses RunStrategy.ColdStart to highlight JIT compilation impact.

MethodinputMeanErrorStdDevMedianRatioRatioSD
RegexWithNewRegexStringCompileda@b.c57.66 us181.22 us534.34 us3.200 us18.12183.58
RegexWithStaticPartialMethoda@b.c25.86 us76.15 us224.52 us2.750 us8.1277.15
        
RegexWithNewRegexStringCompiledanoth(…)mail@ [22]66.36 us211.66 us624.07 us2.600 us25.61261.19
RegexWithStaticPartialMethodanoth(…)mail@ [22]24.67 us68.12 us200.86 us4.150 us9.5284.09
        
RegexWithNewRegexStringCompiledfirst(…)k.com [26]67.96 us218.74 us644.97 us2.800 us24.11241.20
RegexWithStaticPartialMethodfirst(…)k.com [26]37.99 us98.01 us288.99 us7.700 us13.48108.11
        
RegexWithNewRegexStringCompiledinvalid-email61.08 us197.36 us581.92 us2.400 us24.27239.67
RegexWithStaticPartialMethodinvalid-email24.28 us71.96 us212.17 us2.550 us9.6587.39
        
RegexWithNewRegexStringCompiledtest@57.73 us185.14 us545.88 us2.000 us27.12274.84
RegexWithStaticPartialMethodtest@23.39 us67.96 us200.37 us2.400 us10.99100.90
        
RegexWithNewRegexStringCompileduser@domain64.72 us206.86 us609.94 us2.300 us25.15257.44
RegexWithStaticPartialMethoduser@domain34.03 us102.84 us303.22 us2.900 us13.23128.00
        
RegexWithNewRegexStringCompiledvery.(…)e.com [60]63.45 us203.23 us599.24 us2.950 us20.72203.79
RegexWithStaticPartialMethodvery.(…)e.com [60]45.92 us136.02 us401.05 us4.100 us15.00136.40

Results with AOT compilation

With AOT compilation enabled:

MethodinputMeanErrorStdDevMedianRatioRatioSD
RegexWithNewRegexStringCompileda@b.c174.94 ns1.267 ns1.058 ns175.21 ns1.000.01
RegexWithStaticPartialMethoda@b.c55.04 ns0.246 ns0.218 ns55.04 ns0.310.00
        
RegexWithNewRegexStringCompiledanoth(…)mail@ [22]110.63 ns1.908 ns1.784 ns109.74 ns1.000.02
RegexWithStaticPartialMethodanoth(…)mail@ [22]41.26 ns0.668 ns0.592 ns41.15 ns0.370.01
        
RegexWithNewRegexStringCompiledfirst(…)k.com [26]241.25 ns3.879 ns3.439 ns240.85 ns1.000.02
RegexWithStaticPartialMethodfirst(…)k.com [26]78.50 ns1.152 ns1.077 ns78.35 ns0.330.01
        
RegexWithNewRegexStringCompiledinvalid-email87.19 ns1.743 ns3.188 ns85.76 ns1.000.05
RegexWithStaticPartialMethodinvalid-email40.07 ns0.740 ns0.692 ns39.96 ns0.460.02
        
RegexWithNewRegexStringCompiledtest@75.39 ns1.193 ns0.996 ns75.13 ns1.000.02
RegexWithStaticPartialMethodtest@42.26 ns0.333 ns0.295 ns42.28 ns0.560.01
        
RegexWithNewRegexStringCompileduser@domain146.84 ns2.241 ns1.987 ns146.20 ns1.000.02
RegexWithStaticPartialMethoduser@domain60.37 ns0.656 ns0.548 ns60.36 ns0.410.01
        
RegexWithNewRegexStringCompiledvery.(…)e.com [60]363.17 ns4.685 ns6.255 ns360.38 ns1.000.02
RegexWithStaticPartialMethodvery.(…)e.com [60]104.79 ns1.623 ns1.356 ns104.35 ns0.290.01

Source-generated regex with [GeneratedRegex] consistently outperforms RegexOptions.Compiled by 2–3.5× in AOT-compiled scenarios, making it the clear choice for modern .NET applications where both performance and native compilation matter.

This post is licensed under CC BY 4.0 by the author.

View all certificates in User store

-