¿Alguien ha implementado un analizador Regex y / o Xml alrededor de StringBuilders o Streams?

Estoy creando un cliente de prueba de estrés que martilla los servidores y analiza las respuestas utilizando tantos hilos como el cliente puede reunir. Constantemente me encuentro estrangulado por la recolección de basura (y / o la falta de la misma), y en la mayoría de los casos, se trata de cadenas que estoy instanciando solo para pasarlas a un Regex o una rutina de análisis de Xml.

Si descompilas la clase Regex, verás queinternamente, usa StringBuilders para hacer casi todo, pero no puedespasar es un generador de cadena; es útil que se sumerja en métodos privados antes de comenzar a usarlos, por lo que los métodos de extensión tampoco lo resolverán. Estás en una situación similar si deseas obtener un gráfico de objetos del analizador en System.Xml.Linq.

Este no es un caso de sobre-optimización anticipada pedante. He mirado elReemplazos de Regex dentro de un StringBuilder Pregunta y otros. También he perfilado mi aplicación para ver de dónde vienen los techos y cómo usarlos.Regex.Replace() De hecho, ahora está introduciendo una sobrecarga significativa en una cadena de métodos en la que estoy tratando de llegar a un servidor con millones de solicitudes por hora y examinar las respuestas XML en busca de errores y códigos de diagnóstico incrustados. Ya me he deshecho de casi todas las demás ineficiencias que están limitando el rendimiento, e incluso he eliminado gran parte de los gastos generales de Regex extendiendo StringBuilder para que busque / reemplace comodines cuando no necesito grupos de captura o referencias inversas, pero me parece que alguien ya habría cerrado una utilidad de análisis de Regex y Xml basada en StringBuilder (o, mejor aún, Stream).

De acuerdo, despiértense, pero ¿tendré que hacerlo yo mismo?

Actualizar: Encontré una solución que redujo el consumo máximo de memoria de varios gigabytes a unos pocos cientos de megas, por lo que lo estoy publicando a continuación. No lo estoy agregando como respuesta porque a) generalmente odio hacer eso, yb) todavía quiero saber si alguien se toma el tiempo de personalizar StringBuilder para hacer Regexes (o viceversa) antes que yo.

En mi caso, no podría usar XmlReader porque el flujo que estoy ingiriendo contiene algún contenido binario no válido en ciertos elementos. Para analizar el XML, tengo que vaciar esos elementos. Anteriormente estaba usando una única instancia Regex compilada estática para realizar la sustitución, y esta memoria se consumía como loca (estoy tratando de procesar ~ 300 documentos de 10KB / s). El cambio que redujo drásticamente el consumo fue:

Agregué el código de esteArtículo sobre extensiones de StringBuilder en CodeProject para el prácticoIndexOf método.Agregué un (muy) crudoWildcardReplace método que permiteuno carácter comodín (* o?) por invocaciónReemplace el uso de Regex con unWildcardReplace() Convocatoria para vaciar los contenidos de los elementos infractores.

Esto es muy sin pretensiones y probado solo en cuanto a mis propios propósitos; Lo habría hecho más elegante y poderoso, pero YAGNI y todo eso, y tengo prisa. Aquí está el 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;
}

Respuestas a la pregunta(3)

Su respuesta a la pregunta