10

我正在构建一个压力测试客户端,它使用客户端可以召集的尽可能多的线程来锤击服务器并分析响应。我经常发现自己受到垃圾收集(和/或缺乏垃圾收集)的限制,在大多数情况下,它归结为我实例化的字符串,只是为了将它们传递给正则表达式或 Xml 解析例程。

如果你反编译 Regex 类,你会在内部看到,它使用 StringBuilders 来完成几乎所有事情,但你不能它传递给字符串生成器;它有助于在开始使用私有方法之前深入研究它们,因此扩展方法也无法解决它。如果您想从 System.Xml.Linq 中的解析器中获取对象图,您会遇到类似的情况。

这不是迂腐的提前过度优化的情况。我查看了 StringBuilder问题和其他问题中的正则表达式替换。我还分析了我的应用程序以查看天花板的来源,并使用Regex.Replace()now 确实在方法链中引入了大量开销,在该方法链中,我试图以每小时数百万个请求访问服务器,并检查 XML 响应中的错误和嵌入式诊断代码。我已经摆脱了几乎所有其他限制吞吐量的低效率,我什至通过扩展 StringBuilder 来在不需要捕获组或反向引用时进行通配符查找/替换,从而减少了很多正则表达式开销,但在我看来,现在有人已经完成了一个基于 Regex 和 Xml 解析实用程序的自定义 StringBuilder(或者更好的是 Stream)。

好的,所以咆哮,但我必须自己做这个吗?

更新:我找到了一种解决方法,可以将峰值内存消耗从数 GB 降低到数百兆,因此我将其发布在下面。我没有将其添加为答案,因为 a)我通常讨厌这样做,并且 b)我仍然想知道是否有人在我做之前花时间自定义 StringBuilder 来执行正则表达式(反之亦然)。

就我而言,我无法使用 XmlReader,因为我正在摄取的流在某些元素中包含一些无效的二进制内容。为了解析 XML,我必须清空这些元素。我以前使用单个静态编译的 Regex 实例来进行替换,这会像疯了一样消耗内存(我正在尝试处理 ~300 10KB 文档/秒)。大幅减少消费的变化是:

  1. 在 CodeProject 上添加了这篇 StringBuilder Extensions 文章中的代码,以获得方便的IndexOf方法。
  2. 我添加了一个(非常)粗略的WildcardReplace方法,每次调用允许一个通配符(* 或?)
  3. 我用WildcardReplace()调用清空违规元素的内容替换了正则表达式的用法

这是非常不漂亮的,并且仅根据我自己的目的进行了测试;我会让它变得更加优雅和强大,但是 YAGNI 和所有这些,我很着急。这是代码:

/// <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;
}
4

3 回答 3

1

XmlReader 是一个基于流的 XML 解析器。请参阅http://msdn.microsoft.com/en-us/library/756wd7zs.aspx

于 2012-07-18T02:15:25.920 回答
1

Mono 项目已将其核心库的许可证转换为 MIT X11 许可证。如果您需要创建一个针对特定应用程序的性能定制的正则表达式库,您应该能够从Mono 的System library实现的最新代码开始。

于 2012-07-18T03:25:23.237 回答
1
于 2019-02-22T05:27:58.837 回答