Alguém já implementou um analisador Regex e / ou Xml em torno de StringBuilders ou Streams?
Estou construindo um cliente de teste de estresse que martela os servidores e analisa as respostas usando o máximo de threads que o cliente pode reunir. Eu estou constantemente me encontrando limitado pela coleta de lixo (e / ou falta dela), e na maioria dos casos, isso se resume a seqüências de caracteres que estou instanciando apenas para passá-las para um Regex ou uma rotina de análise Xml.
Se você descompilar a classe Regex, verá queinternamente, ele usa StringBuilders para fazer quase tudo, mas você não podepassar é um construtor de string; É útil mergulhar em métodos privados antes de começar a usá-los, portanto os métodos de extensão também não irão resolvê-los. Você está em uma situação semelhante se quiser obter um gráfico de objeto do analisador em System.Xml.Linq.
Este não é um caso de otimização excessiva pedante. Eu olhei para oSubstituições de Regex dentro de um StringBuilder pergunta e outros. Eu também perfilei meu aplicativo para ver de onde os tetos estão vindo e usandoRegex.Replace()
Agora, de fato, estamos introduzindo uma sobrecarga significativa em uma cadeia de métodos em que estou tentando atingir um servidor com milhões de solicitações por hora e examinar as respostas XML em busca de erros e códigos de diagnóstico incorporados. Eu já me livrei de quase todas as outras ineficiências que estão estrangulando o throughput, e eu até cortei muito da sobrecarga de Regex estendendo o StringBuilder para localizar / substituir curingas quando não preciso de grupos de captura ou referências anteriores, mas parece-me que alguém teria envolvido um utilitário de análise Regex e Xml personalizado com o StringBuilder (ou melhor ainda, o Stream).
Ok, tão falante, mas eu vou ter que fazer isso sozinho?
Atualizar: Eu encontrei uma solução que reduziu o consumo de memória de pico de vários gigabytes para algumas centenas de megas, então estou postando abaixo. Eu não estou adicionando isso como uma resposta porque a) Eu geralmente odeio fazer isso, eb) eu ainda quero descobrir se alguém toma o tempo para customizar StringBuilder para fazer Regexes (ou vice-versa) antes de eu fazer.
No meu caso, não pude usar XmlReader porque o fluxo que estou ingerindo contém algum conteúdo binário inválido em determinados elementos. Para analisar o XML, tenho que esvaziar esses elementos. Eu estava usando anteriormente uma única instância Regex compilada estática para fazer a substituição, e isso consumia memória como louco (estou tentando processar ~ 300 10KB docs / seg). A mudança que reduziu drasticamente o consumo foi:
Eu adicionei o código desteArtigo de Extensões StringBuilder no CodeProject para o práticoIndexOf
método.Eu adicionei um (muito) brutoWildcardReplace
método que permite1 caractere curinga (* ou?) por invocaçãoSubstituí o uso do Regex por umWildcardReplace()
chamar para esvaziar o conteúdo dos elementos ofensivosIsso é muito despretensioso e testado apenas no que diz respeito aos meus propósitos; Eu teria feito isso mais elegante e poderoso, mas YAGNI e tudo mais, e estou com pressa. Aqui está o código:
/// <summary>
/// Performs basic wildcard find and replace on a string builder, observing one of two
/// wildcard characters: * matches any number of characters, or ? matches a single character.
/// Operates on only one wildcard per invocation; 2 or more wildcards in <paramref name="find"/>
/// will cause an exception.
/// All characters in <paramref name="replaceWith"/> are treated as literal parts of
/// the replacement text.
/// </summary>
/// <param name="find"></param>
/// <param name="replaceWith"></param>
/// <returns></returns>
public static StringBuilder WildcardReplace(this StringBuilder sb, string find, string replaceWith) {
if (find.Split(new char[] { '*' }).Length > 2 || find.Split(new char[] { '?' }).Length > 2 || (find.Contains("*") && find.Contains("?"))) {
throw new ArgumentException("Only one wildcard is supported, but more than one was supplied.", "find");
}
// are we matching one character, or any number?
bool matchOneCharacter = find.Contains("?");
string[] parts = matchOneCharacter ?
find.Split(new char[] { '?' }, StringSplitOptions.RemoveEmptyEntries)
: find.Split(new char[] { '*' }, StringSplitOptions.RemoveEmptyEntries);
int startItemIdx;
int endItemIdx;
int newStartIdx = 0;
int length;
while ((startItemIdx = sb.IndexOf(parts[0], newStartIdx)) > 0
&& (endItemIdx = sb.IndexOf(parts[1], startItemIdx + parts[0].Length)) > 0) {
length = (endItemIdx + parts[1].Length) - startItemIdx;
newStartIdx = startItemIdx + replaceWith.Length;
// With "?" wildcard, find parameter length should equal the length of its match:
if (matchOneCharacter && length > find.Length)
break;
sb.Remove(startItemIdx, length);
sb.Insert(startItemIdx, replaceWith);
}
return sb;
}