Кто-нибудь реализовал парсер Regex и / или Xml вокруг StringBuilders или Streams?
Я создаю клиент для стресс-тестирования, который отбивает серверы и анализирует ответы, используя столько потоков, сколько может собрать клиент. Я постоянно оказываюсь задушенным сборщиком мусора (и / или его отсутствием), и в большинстве случаев это сводится к строкам, которые я создаю только для того, чтобы передать их в процедуру синтаксического анализа Regex или Xml.
Если вы декомпилируете класс Regex, вы увидите, чтоinternally, он использует StringBuilders, чтобы делать почти все, но вы не можетеpass это струнный строитель; он услужливо погружается в приватные методы перед тем, как начать их использовать, поэтому методы расширения также не решат его. Вы находитесь в аналогичной ситуации, если хотите получить граф объектов из синтаксического анализатора в System.Xml.Linq.
Это не случай педантичной чрезмерной оптимизации заранее. Я посмотрел наRegex замены внутри StringBuilder вопрос и др. Я также профилировал свое приложение, чтобы увидеть, откуда берутся потолки, и используюRegex.Replace()
сейчас действительно вносятся значительные издержки в цепочку методов, где я пытаюсь поразить сервер миллионами запросов в час и проверить ответы XML на наличие ошибок и встроенных диагностических кодов. Я уже избавился практически от любой другой неэффективности, которая ограничивает пропускную способность, и я даже сократил многие накладные расходы на Regex, расширив StringBuilder для выполнения поиска / замены с помощью подстановочных знаков, когда мне не нужны группы захвата или обратные ссылки, но мне кажется, что кто-то уже обернул пользовательскую утилиту синтаксического анализа Regex и Xml на основе StringBuilder (или, еще лучше, Stream).
Хорошо, так разглагольствовать, но я собираюсь сделать это сам?
Update: Я нашел обходной путь, который снизил пиковое потребление памяти с нескольких гигабайт до нескольких сотен мегабайт, поэтому я разместил его ниже. Я не добавляю его в качестве ответа, потому что а) я вообще ненавижу это делать и б) я все еще хочу выяснить, тратит ли кто-нибудь время, чтобы настроить StringBuilder для выполнения регулярных выражений (или наоборот), прежде чем я это сделаю.
В моем случае я не мог использовать XmlReader, потому что поток, который я принимаю, содержит недопустимый двоичный контент в определенных элементах. Чтобы разобрать XML, я должен очистить эти элементы. Ранее я использовал один статический скомпилированный экземпляр Regex для замены, и это потребляло память как безумный (я пытаюсь обработать ~ 300 10 КБ документов / сек). Изменение, которое резко сократило потребление, было:
I added the code from this StringBuilder Extensions article on CodeProject for the handyIndexOf
method.
I added a (very) crude WildcardReplace
method that allows one wildcard character (* or ?) per invocation
I replaced the Regex usage with a WildcardReplace()
call to empty the contents of the offending elements
Это очень неуместно и проверено только настолько, насколько этого требуют мои собственные цели; Я бы сделал его более элегантным и мощным, но ЯГНИ и все такое, и я тороплюсь. Вот код:
/// <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;
}