LOGO 首页 OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 技术文档 其他文档  
 
网站管理员

.NET 10 的正则性能现在什么水平?我拿它和 Go、Python、C++、PCRE2 测了一轮

freeflydom
2026年7月3日 9:49 本文热度 54

前两天看到 apocelipes 写的这篇文章:

https://www.cnblogs.com/apocelipes/p/21055314

文章测了 C++ 标准库、PCRE2、Go、Python,还有一个 Go 的代码生成方案。结论挺有意思,不过我看完第一反应是:怎么没有 .NET?

作为一个长期写 .NET 的人,看到这种正则性能对比没有 .NET,总觉得桌上少了个熟人。尤其是这几年 .NET 的 Regex 引擎其实变化很大,GeneratedRegexNonBacktracking、各种查找优化都已经不是当年那套印象了。所以我就顺手搭了一个仓库,想看看 .NET 10 放进去之后大概会在什么位置。

代码在这里:

https://github.com/sdcb/regex-perf-test

先说结论:在我这份公开合成的数据集上,.NET 10 的 GeneratedRegex 排第一

当然,这句话有前提,后面会说。

为什么不用原文数据?

原文最大的问题不是代码写得怎样,而是测试数据没有给。

正则性能很吃输入分布。比如:

  • 每行有多长
  • 命中比例是多少
  • TSLA 出现在行首还是行尾
  • 不命中的行是不是有很多“差一点命中”的内容
  • 引号和转义字符有多少
  • .*? 中间到底要跨多少字符

这些东西一变,结果就可能变。

所以我没有试图“复现原文结果”。原文数据不公开,就没法严肃复现。我这里做的是另一件事:用公开、确定性的规则合成一份数据,然后把所有代码放出来,让别人可以重新跑。

数据生成脚本在仓库里,规则很简单:

  • 生成约 1GB 的文本文件
  • 每行是一个 JSON string literal
  • JSON string 里面再放一个紧凑 JSON object
  • 每 20 行放一条 TSLA,也就是大约 5% 命中
  • 固定随机种子,确保别人生成出来的数据一致

实际这次生成的数据是:

bytes: 1,073,741,855
lines: 4,624,532
expected matches: 231,227
pattern: \"TSLA.*?\"
seed: 20260702

所有实现都先把文件读进内存,然后只统计内存中扫描匹配的时间。读取时间也记录了,但不参与排序。

测了哪些实现?

这次一共测了 10 个:

case说明
MSVC std::regexVisual Studio 18 / MSVC STL
MinGW std::regexGCC 16.1.0 / libstdc++
PCRE2vcpkg PCRE2 10.47
PCRE2 JIT显式 pcre2_jit_match
Python rePython 3.12.4
Go regexpGo 1.26.2
.NET Regex普通 Regex,构造一次复用
.NET CompiledRegexOptions.Compiled,构造一次复用
.NET GeneratedRegexsource generator 生成的正则
.NET NonBacktrackingRegexOptions.NonBacktracking

这里有个小点要说明:普通 new Regex(...) 不是每行都 new 一次,而是构造一次后复用。每行都 new 那属于测错误用法,不是正常业务热路径。

.NET 这边大概是这样:

private const string Pattern = "\\\"TSLA.*?\\\"";
private static readonly Regex PlainRegex =
    new(Pattern, RegexOptions.CultureInvariant);
private static readonly Regex CompiledRegex =
    new(Pattern, RegexOptions.CultureInvariant | RegexOptions.Compiled);
private static readonly Regex NonBacktrackingRegex =
    new(Pattern, RegexOptions.CultureInvariant | RegexOptions.NonBacktracking);
[GeneratedRegex(Pattern, RegexOptions.CultureInvariant)]
private static partial Regex InfoLineRegex();

核心循环也没什么花活:

private static long MatchLines(string[] lines, Regex regex)
{
    long matches = 0;
    foreach (var line in lines)
    {
        if (regex.IsMatch(line))
        {
            matches++;
        }
    }
    return matches;
}

C++ 的 PCRE2 JIT 也不是“编译了 JIT 然后还调用普通 match”这种模糊写法,而是明确调用 pcre2_jit_match。完整代码直接看 GitHub 就行,这里不贴一大坨了。

结果

每个 case:

  • warmup 1 次
  • 正式跑 3 次
  • 只统计扫描匹配时间
  • 所有 case 匹配数都必须等于 231,227

结果如下,按平均耗时排序:

排名实现平均单轮扫描
1.NET GeneratedRegex125.566 ms
2.NET Regex170.996 ms
3.NET RegexOptions.Compiled171.583 ms
4.NET RegexOptions.NonBacktracking219.780 ms
5Go regexp303.017 ms
6MSVC std::regex448.698 ms
7PCRE2 JIT612.498 ms
8Python re894.424 ms
9PCRE25,191.270 ms
10MinGW/libstdc++ std::regex26,272.900 ms

.NET GeneratedRegex 三轮分别是:

125.294 ms
125.734 ms
125.669 ms

这个波动很小,所以至少在这份数据上,不像是偶然抖出来的。

另外我一开始也跑过 1MB 的小数据,那个时候 .NET 没这么明显的优势。这个也正常,1MB 太小了,JIT、tiered compilation、缓存状态、计时噪声都能影响结果。到了 1GB 之后,差距就稳定多了。

有几个结果挺有意思

第一,.NET GeneratedRegex 真的很猛。

这个结果对我来说有点爽,但不是完全意外。现在的 .NET Regex 对这类模式优化得确实很激进,source generator 又能把一些工作提前到编译期。这个 pattern 本身也简单,本质上更接近“找一个固定前缀,然后扫到后面的引号”。

第二,RegexOptions.Compiled 这次没赢普通 Regex

两者基本持平,普通 Regex 还略快一点点:

.NET Regex:              170.996 ms
.NET RegexOptions.Compiled: 171.583 ms

所以现在写 .NET,别再机械地觉得 Compiled 一定更快。很多时候你真正应该先考虑的是 [GeneratedRegex]Compiled 不是不能用,而是别把它当成无脑性能开关。

第三,NonBacktracking 也不等于更快。

这次它是 219.780 ms,比普通 .NET Regex 慢一些。NonBacktracking 的重点是避免灾难性回溯、提供更稳的线性行为,不是承诺所有正则都更快。

第四,PCRE2 JIT 这次没有赢。

这和很多人的直觉可能不一样。PCRE2 JIT 很强,但不是每个 pattern、每份数据都会碾压。这个 case 里 .NET 的路径显然更适合。

这里我也特意检查了一下,免得变成“JIT 其实没开”的低级问题:PCRE2 用的是 8-bit API,pcre2_compilepcre2_jit_compilepcre2_match_data_create_from_pattern 都在计时循环外;JIT case 检查了 pcre2_jit_compile 的返回值,失败会直接报错;真正扫描时调用的是 pcre2_jit_match,不是普通的 pcre2_match。所有实现也都是逐行 IsMatch/search,不是某些实现扫全文件、某些实现逐行扫。

所以 PCRE2 JIT 输给 MSVC std::regex 这件事我也觉得值得多看一眼,但目前看不是因为测试代码把 JIT 写错了。更可能是这个 pattern 和这份数据刚好不在 PCRE2 JIT 最舒服的区间。

第五,MinGW/libstdc++ 的 std::regex 还是那个味。

1GB 单轮 26 秒多。这个结果非常突出,突出到我都检查了好几遍匹配数。最后所有实现匹配数一致,所以至少这个 case 下它确实很慢。

怎么复现?

仓库:

https://github.com/sdcb/regex-perf-test

快速冒烟:

.\run.ps1 -SizeMB 10 -Iterations 1 -Warmup 0 -Regenerate

正式跑默认 1GB:

.\run.ps1 -Regenerate

输出在:

results/results.csv
results/report.md
results/raw.jsonl

我本机用到的环境大概是:

.NET runtime: 10.0.9
Go: 1.26.2
Python: 3.12.4
MSVC: 19.51
GCC: 16.1.0
PCRE2: 10.47

转自​https://www.cnblogs.com/sdcb/p/21057904/


该文章在 2026/7/3 9:50:06 编辑过
关键字查询
相关文章
正在查询...
点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
Copyright 2010-2026 ClickSun All Rights Reserved  粤ICP备13012886号-9  粤公网安备44030602007207号