.Net 5 Regex Performance
Tags: csharp, regex, programming, net5
Published: 2021-10-11 17:54:38

Olá pessoal!

Hoje vamos fazer um teste de performance utilizando regex com .net 5.

Para esse teste criaremos um console application com a instalção de 2 pacotes nuget:
```powershell
dotnet new console —name TestePerformanceRegex
dotnet add package BenchmarkDotNet
dotnet add package Bogus
```
BenchmarkDotNet é o pacote responsável por realizar nosso benchmark.

Bogus é o pacote responsável por gerar os dados para nossos testes.

Nosso console app será composto por duas classes:

1. Program.cs
2. TesteRegex.cs

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p7gaauqtwvrgr3u0mqre.png)

Nossa classe Program.cs será dessa forma:

```csharp
class Program
{
	static void Main(string[] args)
	{
		BenchmarkRunner.Run<TesteRegex>();
	}
}
```
Nossa classe TesteRegex.cs deverá ter o annotation **[MemoryDiagnoser]**
```csharp
[MemoryDiagnoser]
public class TesteRegex
{
}
```

Agora vamos criar uma classe Usuário, onde teremos E-mail e Login:
```csharp
public class Usuario
{
    public string Email { get; set; }
    public string Login{ get; set; }
}
```
Agora dentro da classe TesteRegex, vamos fazer o seguinte:
```csharp
private static readonly List<Usuario> FakeUsuarios = new Faker<Usuario>()
	.UseSeed(420)
	.RuleFor(r => r.Email, faker => faker.Person.Email)
	.RuleFor(r => r.Login, faker => faker.Person.UserName)
	.Generate(10);
```
Estamos criando 10 usuários, utilizando o Bogus.

Agora vamos criar uma lista com e-mails validos e e-mails inválidos:
```csharp
private static readonly List<string> emails = 
    FakeUsuarios.Select(x => x.Email)
	    .Concat(FakeUsuarios.Select(x => x.Login)).ToList();
```
Com isso teremos um total de 20 itens na lista e-mails.

Agora faremos os regex abaixo:
```csharp
private static readonly Regex RegexStatico =
    new(@"^[^@\s]+@[^@\s]+\.[^@\s]+$", RegexOptions.IgnoreCase);

private static readonly Regex RegexStaticoCompilado =
    new(@"^[^@\s]+@[^@\s]+\.[^@\s]+$", RegexOptions.IgnoreCase | RegexOptions.Compiled,
        TimeSpan.FromMilliseconds(250));
```
Com isso temos o primeiro Regex sendo estático e o segundo estático e compilado, também foi informado um timeout de 250 milissegundos. Esse timeout deve ser configurado conforme a necessidade da sua aplicação.

Agora iremos criar os métodos responsáveis pelos testes.

Importante ressaltar que todos os métodos tem a anotação de **[Benchmark]**.
```csharp
[Benchmark]
public void IsMatchInternal()
{
    var regex = new Regex(@"^[^@\s]+@[^@\s]+\.[^@\s]+$", RegexOptions.IgnoreCase);

    for (var i = 0; i < emails.Count; i++)
    {
        var email = emails[i];
        var isMatch = regex.IsMatch(email);
    }
}
```
No método acima, estamos declarando o regex internamente e fazendo a validação de todos os e-mails da nossa lista.
```csharp
[Benchmark]
public void IsMatchInternalCompiled()
{
    var regex = new Regex(@"^[^@\s]+@[^@\s]+\.[^@\s]+$", RegexOptions.IgnoreCase | 
					RegexOptions.Compiled);

    for (var i = 0; i < emails.Count; i++)
    {
        var email = emails[i];
        var isMatch = regex.IsMatch(email);
    }
}
```
Já no método acima, estamos declarando o regex internamente e a flag de compilado. 
```csharp
[Benchmark]
public void IsMatchStatic()
{
    for (var i = 0; i < emails.Count; i++)
    {
        var email = emails[i];
        var isMatch = RegexStatico.IsMatch(email);
    }
}
```
Nesse método estamos utilizando a declaração estática RegexStatico ao invés de declararmos internamente no método.
```csharp
[Benchmark]
public void IsMatchStaticCompiled()
{
    for (var i = 0; i < emails.Count; i++)
    {
        var email = emails[i];
        var isMatch = RegexStaticoCompilado.IsMatch(email);
    }
}
```
Finalmente o último método, temos a utilização da declaração estática RegexStaticoCompilado com a flag de compilado.

Agora vamos executar nossa aplicação, é importante ressaltar que devemos executá-la em modo Release.
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yrd06523aqkge3bggdwk.png)
Após executarmos, teremos o resultado no console:
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8s0yk533fc4aomcjofa9.png)
Podemos ver que o método mais performático de todos foi quando utilizamos a declaração estática com a flag de compilado, onde levou 4 microssegundos.

Já o nosso pior cenário foi quando utilizamos a declaração interna e copilada, onde levou 1.9 milissegundos e foi alocado 18KB de memória.

Com alguns ajustes simples podemos ter um ganho expressivo de performance quando estamos trabalhando com regex.

Abaixo link do código no github:
https://github.com/ouell/regex-performance