O que é o SearchValues do .NET
O SearchValues e uma classe introduzida no .NET 8, projetada para buscas altamente otimizadas em strings e spans. Diferente de métodos como string.Contains() ou IndexOfAny(), o SearchValues pre-processa o conjunto de caracteres ou strings que você quer buscar e usa instruções SIMD (Single Instruction Multiple Data) para processar múltiplos valores ao mesmo tempo.
A proposta central e simples: se você vai buscar pelo mesmo conjunto de valores muitas vezes ao longo da vida da aplicação, vale pagar o custo de pre-processamento uma única vez na inicialização e ter buscas subsequentes muito mais rápidas. E exatamente o caso de uso de parsers, validadores de entrada, sanitizadores de HTML e qualquer código que processa grandes volumes de texto repetidamente.
O objeto SearchValues encapsula lógica de matching otimizada para um conjunto fixo de valores. Você cria o objeto uma vez e reutiliza em chamadas de span.IndexOfAny() ou span.ContainsAny(). O resultado e performance bem superior ao loop manual ou ao IndexOfAny com array simples em cenários de alta frequência.
Como funciona o SearchValues internamente
O SearchValues usa uma técnica de otimização chamada probabilistic map combinada com instruções SIMD. Na criação do objeto, o conjunto de caracteres alvo e pre-processado em uma estrutura de dados especializada que permite verificação extremamente rápida de membership.
Durante a busca, o .NET usa instruções como SSE2, AVX2 ou ARM NÉON dependendo da CPU disponível para verificar múltiplos bytes do texto de entrada simultaneamente. Em vez de verificar um caractere por vez, o runtime pode verificar 16 ou 32 caracteres em uma única instrução de CPU.
O ganho de performance e mais pronunciado quando: (1) o texto sendo pesquisado e longo, (2) o conjunto de valores a buscar e médio a grande (3 ou mais itens), e (3) a operação de busca e executada com frequência alta. Para buscas pontuais em strings curtas, o overhead de setup pode superar o ganho.
Declare objetos SearchValues como campos static readonly. A pre-computação e feita na primeira inicialização e o objeto e reusado em todas as chamadas, que é exatamente o padrão de uso ideal.
Principais recursos e casos de uso
O SearchValues suporta dois tipos de busca:
- SearchValues de char: busca por qualquer caractere de um conjunto em uma string ou ReadOnlySpan de char. Ideal para sanitização de HTML, validação de senhas, parsing de CSV e delimitadores
- SearchValues de string: busca por qualquer string de um conjunto em um texto. Disponível a partir do .NET 9. Ideal para detecção de palavras-chave, filtros de conteúdo, parsing de tokens específicos
Os métodos que aceitam SearchValues como parâmetro são extensões de Span e MemoryExtensions: IndexOfAny, ContainsAny, IndexOfAnyExcept e ContainsAnyExcept. Cada um tem sua variante que aceita o objeto SearchValues pre-computado.
Como começar: criação e uso básico
O SearchValues esta disponível a partir do .NET 8 no namespace System.Buffers. Não e necessário instalar nenhum pacote adicional, faz parte do runtime base do .NET.
using System.Buffers;
// Criação: uma vez como campo estático
private static readonly SearchValues CharsHtml =
SearchValues.Create(new char[] { '<', '>', '&' });
// Uso: verificação rápida se o texto tem chars perigosos
public static bool PrecisaSanitizar(ReadOnlySpan texto)
{
return texto.ContainsAny(CharsHtml);
}
// Encontrar posição do primeiro char perigoso
public static int PrimeiroPosicaoPeigosa(ReadOnlySpan texto)
{
return texto.IndexOfAny(CharsHtml);
}O SearchValues de string para buscas de strings completas esta disponível apenas no .NET 9 e posterior. O SearchValues de char para conjuntos de caracteres esta disponível desde o .NET 8.
Exemplo prático: sanitizador de HTML com SearchValues
Um caso de uso real e detectar rapidamente se um texto precisa de sanitização antes de exibir conteúdo gerado por usuário. Com SearchValues, você pode encontrar o primeiro caractere que precisa de escape de forma eficiente:
using System.Buffers;
using System.Text;
public static class HtmlSanitizer
{
private static readonly SearchValues CharsParaEscape =
SearchValues.Create(new char[] { '<', '>', '&' });
public static string Sanitizar(string entrada)
{
// Verificação rápida: precisa de escape?
if (!entrada.AsSpan().ContainsAny(CharsParaEscape))
return entrada; // Retorno rápido para o caso mais comum
// Apenas se encontrou algo suspeito, processa o texto
return ProcessarEscape(entrada);
}
private static string ProcessarEscape(string entrada)
{
var sb = new StringBuilder(entrada.Length + 32);
foreach (char c in entrada)
{
if (c == '<') sb.Append("<");
else if (c == '>') sb.Append(">");
else if (c == '&') sb.Append("&");
else sb.Append(c);
}
return sb.ToString();
}
}O ganho mais importante aqui é o retorno rápido para entradas que não precisam de escape, que é o caso mais comum em produção. O SearchValues verifica o texto inteiro em uma passagem ultra-rápida, enquanto verificações char por char verificariam cada caractere sequencialmente.
Comparação com alternativas
Para entender quando usar SearchValues, compare com as alternativas mais comuns:
string.Contains(char): simples, legível, eficiente para busca de um único caractere. O compilador JIT otimiza bem esse caso. Não use SearchValues para buscar um único caractere, não vale o overhead.
IndexOfAny(char[]): a alternativa clássica para buscar múltiplos caracteres. Funciona bem para conjuntos pequenos de 2 a 3 chars. Com conjuntos maiores ou em hot paths de alta frequência, SearchValues supera IndexOfAny porque usa SIMD internamente enquanto IndexOfAny faz loop simples.
Regex: mais poderoso para padrões complexos, mas com overhead de compilação e execução muito maior. Para busca de caracteres ou strings fixas, SearchValues e muito mais eficiente. Use Regex apenas quando o padrão de busca e realmente complexo com grupos, alternativas e repetidores.
Use BenchmarkDotNet para medir o impacto real antes e depois de migrar para SearchValues no seu código específico. Os ganhos variam muito com o tamanho do texto e frequência das chamadas.
Pontos positivos e limitações
O SearchValues brilha em hot paths de alta frequência: parsers de request HTTP, validadores de entrada em APIs de alto throughput, processadores de texto em batch. Em cenários com milhões de chamadas por segundo, a diferença de performance e significativa e mensurável com benchmarks.
A legibilidade do código com SearchValues e boa se os objetos são declarados perto de onde são usados e com nomes descritivos. O padrão static readonly com nome descritivo e idiomático na comunidade .NET e não adiciona confusão ao código.
As limitações são claras: SearchValues não faz sentido para buscas pontuais em textos curtos, para conjuntos com apenas 1 a 2 chars, ou para código que não esta em hot path. A pre-computação tem custo, e esse custo só e amortizado com muitas chamadas subsequentes.
Casos de uso reais
Parsers de protocolo HTTP: o próprio ASP.NET Core usa SearchValues internamente para parsing de headers HTTP, URLs e corpos de request. E um dos casos de uso que motivou a criação da API no próprio runtime do .NET.
Validadores de entrada de API: APIs que validam campos de texto contra caracteres proibidos, formatos específicos ou palavras reservadas se beneficiam muito de SearchValues no caminho de validação, especialmente em APIs de alta carga.
Sanitizadores de conteúdo: sistemas que processam conteúdo gerado por usuário, como comentários ou posts, precisam verificar e sanitizar HTML antes de salvar ou exibir. SearchValues otimiza o passo de detecção inicial.
Processadores de log em tempo real: sistemas de observabilidade que filtram logs em tempo real por palavras-chave de erro ou padrão específico processam volumes enormes de texto. SearchValues no caminho crítico reduz latência de ingestão consideravelmente.
Dicas e boas práticas
Quando possível, trabalhe com ReadOnlySpan em vez de string. Isso evita alocações desnecessárias e permite usar SearchValues sem copiar dados. O compilador faz a conversão de string para span automaticamente na maioria dos contextos.
Combine SearchValues com IndexOfAnyExcept para encontrar o primeiro caractere que NÃO esta no conjunto permitido. E útil para validação de formato: você encontra onde a entrada desvia do padrão esperado.
Não crie objetos SearchValues dentro de loops ou métodos frequentemente chamados. O objeto deve ser criado uma vez e reutilizado. Criar SearchValues a cada chamada e pior que usar IndexOfAny simples por causa do custo de pre-computação.
O SearchValues de string do .NET 9 usa o algoritmo Aho-Corasick internamente para busca multi-string otimizada. Para detecção de palavras-chave em textos longos, e muito mais eficiente do que múltiplas chamadas a IndexOf sequenciais.
Vale a pena usar SearchValues?
Para código em hot path que processa texto com alta frequência: sim, definitivamente. O ganho de performance e real, mensurável com benchmarks, e o código não fica muito mais complexo quando bem organizado com campos static readonly e nomes descritivos.
Para o caso geral de código de negócios que processa texto ocasionalmente: provavelmente não. Use string.Contains() e IndexOfAny() que são suficientes para a maioria dos casos e tem melhor legibilidade sem a necessidade de conhecimento da API de SearchValues.
O próximo passo e identificar os hot paths de processamento de texto na sua aplicação (usando profiler como dotnet-trace ou Visual Studio Profiler), medir o tempo atual, migrar para SearchValues, e medir novamente. Só migre onde os dados confirmam o beneficio real.
Comentários
Deixar um comentárioVocê precisa ter uma conta no CuritibaBlog para comentar.