正则表达式引擎比较(翻译自:A comparison of regex engines)
原文: A comparison of regex engines – Rust Leipzig
引言
正则表达式(或简称regex)通常用于模式搜索算法。 有许多不同的正则表达式引擎提供不同的表达式支持、性能约束和语言绑定。 基于 John Maddock 之前的工作 (regex comparison)和 sljit 项目( regex comparison),这里概述下几个活跃开发的引擎的性能。
搭建测试
硬件
这里的性能仅在我的戴尔笔记本上测试。它并不是最新的,但这并不重要,因为我对所有引擎使用相同的硬件,并且我对不同引擎性能比较的结果感兴趣。 这里是硬件信息:
- Chassis: Dell Latitude E7450
- CPU: Intel® Core™ i5-5300U
- RAM: 16GB
- SSD: Samsung PM85 256GB
软件
这里并非使用最新的软件,但也比 Ubuntu 16.04 系统默认的软件包更新了
- GCC 6.2.0
- Rustc 1.16.0 and 1.17.0-nightly
我想知道用不同引擎匹配以下每一项的执行时间:
Twain(?i)Twain[a-z]shingHuck[a-zA-Z]+|Saw[a-zA-Z]+\b\w+nn\b[a-q][^u-z]{13}xTom|Sawyer|Huckleberry|Finn(?i)Tom|Sawyer|Huckleberry|Finn.{0,2}(Tom|Sawyer|Huckleberry|Finn).{2,4}(Tom|Sawyer|Huckleberry|Finn)Tom.{10,25}river|river.{10,25}Tom[a-zA-Z]+ing\s[a-zA-Z]{0,12}ing\s([A-Za-z]awyer|[A-Za-z]inn)\s["'][^"']{0,30}[?!\.][\"']\u221E|\u2713\p{Sm}
也许以上表达式集合不够代表性,但也足以提供一个参考.
为了测量性能,我修改了 sljit 项目现有的基准测试工具。 该工具可在 github 上找到: regex-performance. sljit 项目的基础工具已经支持以下正则表达式引擎:
- Oniguruma, v6.1.3
- RE2
- Tre
- PCRE2, v10.23
这里我多加2种引擎:
- Hyperscan, v4.4.1
- Rust regex crate, v0.2.1
PCRE2
Perl 兼容正则表达式 (PCRE) 是一个正则表达式 C 库,其灵感来自于 Perl 编程语言中的正则表达式功能。 PCRE2 是 PCRE 库修订后的 API 的名称。
除了标准匹配算法之外,PCRE2 还附带了一种基于确定性有限自动机 (DFA) 的替代算法,该算法运行方式不同且不与 Perl 兼容。 手册页中提供了详细的描述。
此外PCRE2还提供了重量级优化:即时(JIT)编译可以大大加快模式匹配速度。
为了获得可比较的结果,必须使用配置选项 --enable-unicode 启用 Unicode 支持。 JIT 功能是可选的,必须配合选项 --enable-jit 启用。
Hyperscan
Hyperscan 是 01.org 开源项目:
Hyperscan 是一个高性能的多正则表达式匹配库。 它遵循常用的 libpcre 库的正则表达式语法,但作为一个独立的库且用 C 编写了 API。Hyperscan利用混合自动机技术,可以同时匹配大量的正则表达式,以及在数据流中匹配正则表达式。
Hyperscan是经过10多年开发的成熟的库。Hyperscan着重的是x86平台,并且该库使用硬件加速器(如AVX)来优化吞吐量。
默认情况下,Hyperscan不考虑匹配的起始位置。要获取匹配的起始位置,需要在编译模式时设置标志HS_FLAG_SOM_LEFTMOST。这个标志会带来一些性能损失,但是在需要可比较结果时是必需的。
Rust 正则表达式箱
Rust 箱是“库”或“包”的同义词。Rust 正则表达式箱提供了解析、编译和执行正则表达式的函数:
它的语法类似于 Perl 风格的正则表达式,但缺少一些功能,例如环视和反向引用。 但带来的好处是,所有搜索的时间复杂度都与正则表达式和搜索文本的长度成线性关系。
除了Rust crate之外,所有引擎都是使用C或C++编写的,包括测试工具。使用的引擎必须有C绑定,因此需要一个接口来调用Rust函数。该解决方案利用Rust的FFI(外部函数接口)构建一个静态库,该库只会计算给定表达式的匹配次数。完整的库包含3个函数,总共不到50行代码。获取匹配项的主要Rust函数是::
#[no_mangle]
pub extern fn regex_matches(raw_exp: *mut Regex, p: *const u8, len: u64) -> u64 {let exp = unsafe { Box::from_raw(raw_exp) };let s = unsafe { slice::from_raw_parts(p, len as usize) };let findings = exp.find_iter(s).count();Box::into_raw(exp);findings as u64
}
该函数接受一个先前编译的表达式的原始指针(raw_exp)、一个输入C字符串的原始指针(p)以及输入字符串的长度(len)。首先,函数从相应的原始指针中获取编译后的表达式和输入字符串。将原始指针转换为类型是不安全的操作,因此代码部分必须用unsafe{}包装起来。然后,通过调用exp.find_iter(s).count()来获取匹配项的数量。为了在后续函数调用中使用编译后的表达式,再次获取表达式的原始指针。这样做的结果是,在返回后,表达式的生命周期仍然存在。最后,该函数将匹配项的数量作为64位值返回给调用者。
对应的C函数原型是:
struct Regex; // anonymous declarationextern uint64_t regex_matches(struct Regex const * const exp, uint8_t * const str, uint64_t str_len);
结果
在工具构建路径执行以下命令以获取测试结果:
./src/regex_perf -f ../3200.txt -o results.csv
工具将细节打印如下,每个引擎的结果保存到 results.csv. 最后还打印了结果的简要总结:
Total Results:
[ pcre] time: 12626.7 ms, score: 8 points,
[ pcre-dfa] time: 14135.2 ms, score: 0 points,
[ pcre-jit] time: 1050.6 ms, score: 47 points,
[ re2] time: 946.1 ms, score: 26 points,
[ onig] time: 2475.8 ms, score: 4 points,
[ tre] time: 10508.4 ms, score: 0 points,
[ hscan] time: 299.7 ms, score: 72 points,
[rust_regex] time: 3681.5 ms, score: 47 points,
Timings
根据CSV文件我做了一些分析。首先我计算了每个引擎的总体执行时间。详见下图:

Hyperscan是最快的引擎,总执行时间约为300毫秒(比第二名少约3倍),而Rust的正则表达式库在排名中位列第5,总执行时间约为3700毫秒。看来Rust的正则表达式库并不是最快的解决方案。
但是,如果一个表达式非常慢会发生什么呢?这个测试会扭曲引擎的整体结果。因此,我实现了一个简单的结果评分系统。对于每个测试,最快的引擎可以得到5分,第二名得到4分,依此类推。这限制了单个慢表达式的影响。以下图表显示了每个引擎的得分点数:

Hyperscan仍然是第一名,但Rust的正则表达式库与PCRE2-JIT并列第二。结果比绝对时间看起来更好,但似乎有一个或多个表达式的执行时间很慢。
因此,现在是时候查看每个表达式的结果了。以下图表将所有引擎每个表达式的平均时间与Rust的测量值进行了比较。次要的y轴显示了Rust值与平均值的比例,以百分比表示。
.

红色曲线有3个主要的峰值,即正则表达式库性能不佳的表达式。这些表达式是:
[a-q][^u-z]{13}x∞|✓(?i)Twain
特别是这三个表达式中的第一个执行非常缓慢。
改进
根据基准测试的初步结果,我开了一个投票 rust-lang/regex/350 来汇报我的发现以获得些反馈。Andrew Gallant(化名BurntSushi)给了我很好的反馈和一些改进建议。
其中一项改进是使用正则表达式库的SIMD功能。这个功能目前在Rust Nightly构建中可用,因此我需要安装Nightly工具链。我调整了项目的CMake脚本,以检测是否使用了Nightly编译器并支持SIMD功能。因此,可以使用rustup default nightly-x86_64-unknown-linux-gnu切换Rust工具链,并重新配置和构建工具以获取新的结果。

图表显示,表达式∞|✓和(?i)Twain通过使用SIMD功能受益,但表达式[a-q][^u-z]{13}x则不受益。这个表达式需要回溯。Rust的正则表达式库使用基于有限状态机(DFA)的算法,缺乏反向引用和回溯功能。.
匹配
Regarding the found matches I found some deviations. At first, the libraries oniguruma and tre do not support Unicode category expressions like \p{Sm}. This expression matches all mathematical symbols like = or |. The Rust regex crate matches additionally the symbol ∞.
Hyperscan returns more matches than other engines, e.g. 977 for the expression Huck[a-zA-Z]+|Saw[a-zA-Z]+ whereas all other engines are finding 262 matches. Hyperscan reports all matches. The expression Saw[a-zA-Z]+ returns the following matches for input Sawyer:
从找到的匹配项中我发现了一些差异。首先,oniguruma和tre库不支持Unicode类别表达式,如\p{Sm}。这个表达式匹配所有的数学符号,比如=或|。而Rust的正则表达式库还额外匹配了符号∞。
Hyperscan返回的匹配项比其他引擎多,例如对于表达式Huck[a-zA-Z]+|Saw[a-zA-Z]+,Hyperscan返回了977个匹配项,而其他引擎只找到了262个匹配项。Hyperscan报告了所有的匹配项。对于输入"Sawyer",表达式Saw[a-zA-Z]+返回了以下匹配项:
- Sawy
- Sawye
- Sawyer
其他所有引擎只报告了一个匹配项:Sawy(非贪婪语义)或Sawyer(贪婪语义)。
结论
Rust正则表达式库已经推出约2年了,但它趋向于超越像PCRE2和Hyperscan这样成熟的引擎。根据使用的表达式,Rust正则表达式库是进行模式匹配的好选择。感谢正则表达式库的所有贡献者以及他们令人惊叹的工作。.
相关工作
regex crate包含自己的基准测试框架,其中包含许多表达式,并支持以下功能:
- PCRE
- PCRE2
- RE2
- Oniguruma
- TCL
这个基准测试可以用来从另一个角度评估引擎的性能。请查看crates存储库中的bench子目录。
相关文章:
正则表达式引擎比较(翻译自:A comparison of regex engines)
原文: A comparison of regex engines – Rust Leipzig 引言 正则表达式(或简称regex)通常用于模式搜索算法。 有许多不同的正则表达式引擎提供不同的表达式支持、性能约束和语言绑定。 基于 John Maddock 之前的工作 (regex comparison)和…...
后端Linux软件安装大全[JDK、Tomcat、MySQL、Irzsz、Git、Maven、Redis、Nginx...持续更新中]
文章目录 前言1.软件安装方式2.安装jdk3.安装Tomcat4.安装MySQL5.安装lrzsz6. 安装Git7. 安装Maven8. 安装Redis9. 安装Nginx 总结 前言 为了巩固所学的知识,作者尝试着开始发布一些学习笔记类的博客,方便日后回顾。当然,如果能帮到一些萌新…...
C++ Dijkstra 最短路径求解算法的两种实现方案
迪杰斯特拉算法(Diikstra) 是由荷兰计算机科学家狄克斯特拉于1959 年提出的,因此又叫狄克斯特拉算法。 核心思想,搜索到某一个顶点后,更新与其相邻顶点的权重。顶点权重的数据含义表示从起始点到此点的最短路径长度(也就是经过的…...
因存在色情内容,夸克被罚50万元
媒体经济的繁荣、自媒体、直播等各种形式的信息传播疯狂发展,但是各种形式的信息资源大规模生产时,“色情”,“暴力”的图像和视频不可控的滋生,特别是某些 APP 或浏览器。一旦打开,满屏都是“哥哥,快来啊”…...
汽车EDI:福特Ford EDI项目案例
项目背景 福特(Ford)是世界著名的汽车品牌,为美国福特汽车公司(Ford Motor Company)旗下的众多品牌之一。此前的文章福特FORD EDI需求分析中,我们已经了解了福特Ford EDI 的大致需求,本文将会介…...
正则表达式的使用实例
正则表达式的使用实例 1- 表示2- 实例 1- 表示 1, [:digit:] 表示0-9全部十个数字 //等价于 0123456789, 而不等价于[0123456789] 2, [[:digit:]] 表示任意一个数字 \{m,n\} 表示其前面的字符出现最少m次,最多n次的情况 \{3,\} 其前面的字符出…...
STM智能小车——OLED实现测速小车
目录 1. 测速模块 2. 测试原理和单位换算 3. 定时器和中断实现测速开发和调试代码 4. 小车速度显示在OLED屏 1. 测速模块 用途:广泛用于电机转速检测,脉冲计数,位置限位等。有遮挡,输出高电平;无遮挡,输出低电平接线…...
pod基本概念
目录 pod基本概念 pause容器 Pod分类: Pod容器的分类 1、基础容器(infrastructure container) 2、初始化容器(initcontainers) 3、应用容器(Maincontainer) 镜像拉取策略(im…...
SQL Server 中定时调度调用存储过程
要在SQL中定时调度调用存储过程,你可以使用SQL Server代理(如果你正在使用SQL Server数据库)。下面是一些步骤来配置SQL Server代理以定时调度调用存储过程: 打开SQL Server Management Studio (SSMS) 并连接到你的SQL Server实例…...
SpringCloud(三) Ribbon负载均衡
SpringCloud(二) Eureka注册中心的使用-CSDN博客 在SpringCloud(二)中学习了如何通过Eureka实现服务的注册和发送,从而通过RestTemplate实现不同微服务之间的调用,加上LoadBalance注解之后实现负载均衡,那负载均衡的原理是什么呢? 目录 一, 负载均衡 1.1 负载均衡原理 1.2 源…...
vue2:路由前置守卫无法获取到this.$store.state.xxx
在获取到vuex的数据时候,想在router目录下的index.js文件去获取到vuex仓库中声明的全局变量,但是通过this.$store.stote.xxx去获取的时候,报错提示:$store未定义 一、store/index.js const store new Vuex.Store({state: {// 属…...
Unity的碰撞检测(五)
温馨提示:本文基于前一篇“Unity的碰撞检测(四)”继续探讨两个游戏对象具备刚体的BodyType均为Dynamic,但是Collision Detection属性不同的碰撞检测,阅读本文则默认已阅读前文。 (一)测试说明 在基于两…...
Flutter笔记:Flutter的应用生命周期状态(lifecycleState)管理
Flutter笔记 Flutter的应用生命周期状态(lifecycleState)管理 作者:李俊才 (jcLee95):https://blog.csdn.net/qq_28550263 邮箱 :291148484163.com 本文地址:https://blog.csdn.net/…...
代碼隨想錄算法訓練營|第五十四天|300.最长递增子序列、674. 最长连续递增序列、718. 最长重复子数组。刷题心得(c++)
讀題 300.最长递增子序列 看完代码随想录之后的想法 思想上很簡單,dp[i]表示i之前的包括i的numbers[i]節尾的最長上升子序列的長度 並且透過兩層迴圈,一層遍歷全部,一層遍歷到i,透過比較當前dp[i]還是dp[j] 1哪個比較大&…...
正点原子嵌入式linux驱动开发——Linux 串口RS232/485/GPS 驱动
串口是很常用的一个外设,在Linux下通常通过串口和其他设备或传感器进行通信,根据 电平的不同,串口分为TTL和RS232。不管是什么样的接口电平,其驱动程序都是一样的,通过外接RS485这样的芯片就可以将串口转换为RS485信号…...
HDFS工作流程和机制
HDFS写数据流程(上传文件) 核心概念--Pipeline管道 HDFS在上传文件写数据过程中采用的一种传输方式。 线性传输:客户端将数据写入第一个数据节点,第一个数据节点保存数据之后再将快复制到第二个节点,第二节点复制给…...
CMMI/ASPICE认证咨询及工具服务
服务概述 质量专家戴明博士的名言“如果你不能描述做事情的过程,那么你不知道你在做什么”。过程是连接有能力的工程师和先进技术的纽带,因此产品开发过程直接决定了产品的质量和研发的效率。 经纬恒润可结合多体系要求,如IATF16949\ISO26262…...
【NI-DAQmx入门】计数器
1.计数器的作用 NI产品的计数器一般来说兼容TTL信号,定义如下:0-0.8V为逻辑低电平,2~5V为高电平,0.8-2V为高阻态,最大上升下降时间为50ns。 计数器可以感测上升沿(从逻辑低到逻辑高的转变)和下降…...
Python爬取读书网的图片链接和书名并保存在数据库中
一个比较基础且常见的爬虫,写下来用于记录和巩固相关知识。 一、前置条件 本项目采用scrapy框架进行爬取,需要提前安装 pip install scrapy# 国内镜像 pip install scrapy -i https://pypi.douban.com/simple 由于需要保存数据到数据库,因…...
js解决加油站
在一条环路上有 n 个加油站,其中第 i 个加油站有汽油 gas[i] 升。 你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。 给定两个整数数组 gas 和 cost &…...
RAR Unlocker 4.0 汉化版:专注 RAR 压缩包锁定 / 解锁,支持查看属性与命令行批量处理,轻量便携,是解决 RAR 锁定问题的优质辅助工具
大家好,我是大飞哥。日常使用 RAR 压缩包时,误操作锁定后会导致文件无法修改、添加或删除,而 WinRAR 本身又不提供便捷的解锁功能,手动处理不仅繁琐还容易损坏压缩包 —— 而RAR Unlocker 4.0 汉化版就是专为解决这些痛点打造的轻…...
如何通过自动化工具高效获取阴阳师游戏资源?完整实践指南
如何通过自动化工具高效获取阴阳师游戏资源?完整实践指南 【免费下载链接】OnmyojiAutoScript Onmyoji Auto Script | 阴阳师脚本 项目地址: https://gitcode.com/gh_mirrors/on/OnmyojiAutoScript 阴阳师自动化工具是一款功能强大的智能辅助应用,…...
从ImageNet到CV落地:深度解读AlexNet的6个工程优化技巧
从AlexNet到现代CV工程:6个历久弥新的优化策略解析 当AlexNet在2012年ImageNet竞赛中以压倒性优势夺冠时,它带来的不仅是准确率的飞跃,更是一套影响深远的工程实践方法论。十年过去,尽管网络架构已迭代数十代,但AlexNe…...
Node.js 轻量级数据库 NeDB 实战指南:从入门到精通
1. 为什么你需要了解NeDB 如果你正在寻找一个轻量级的Node.js数据库解决方案,NeDB绝对值得你花时间研究。作为一个嵌入式数据库,它不需要单独运行数据库服务,数据可以直接存储在内存或磁盘文件中。我在多个小型项目中使用过NeDB,最…...
Windows 11下保姆级安装Isaac Sim 4.5.0与Isaac Lab避坑全记录(含CUDA 12.8配置)
Windows 11下Isaac Sim 4.5.0与Isaac Lab全流程部署指南(RTX 4090实测版) 对于机器人仿真和AI开发领域的从业者来说,NVIDIA Isaac Sim和Isaac Lab无疑是当前最强大的工具组合之一。然而,当我在自己的RTX 4090显卡上首次尝试部署这…...
OpenClaw成本优化方案:ollama GLM-4-7-Flash替代OpenAI API实测
OpenClaw成本优化方案:ollama GLM-4-7-Flash替代OpenAI API实测 1. 为什么需要寻找OpenAI API的替代方案 去年我开始在个人项目中使用OpenClaw实现自动化办公流程时,很快被OpenAI API的token消耗速度震惊了。一个简单的"读取邮件附件-解析内容-生…...
探索电池2RC等效电路模型:从参数辨识到SOC估计
电池2RC等效电路模型,最小二乘法参数辩识,电池端电压误差小,扩展卡尔曼估计SOC精度高。 有文档,数据,视频,仿真图。在电池研究领域,准确建模和参数估计对于理解电池行为至关重要。今天咱就唠唠电…...
TMS320F28P550SJ9实战解析:Sysconfig高效配置SCI多处理器通信模式
1. TMS320F28P550SJ9的SCI通信基础认知 第一次接触TMS320F28P550SJ9的SCI模块时,我花了整整三天才搞明白它的全双工特性。这个看似简单的串行通信接口,实际上藏着不少工程师容易忽略的细节。SCI(Serial Communication Interface)作…...
[260326] x-cmd v0.8.10:跨 Shell 统一配置命令短名;自动装好依赖运行 WhisperLiveKit 实时语音转写
[260326] x-cmd v0.8.10:跨 Shell 统一配置命令短名;自动装好依赖运行 WhisperLiveKit 实时语音转写 开放 shortcut 内部模块,配置命令短名,支持跨 Shell 统一使用whisper 模块新增 livekit 命令,自动装好依赖&#x…...
nlp_gte_sentence-embedding_chinese-large在软件测试用例生成中的应用
nlp_gte_sentence-embedding_chinese-large在软件测试用例生成中的应用 1. 引言 软件测试是确保产品质量的关键环节,但传统的手工编写测试用例方式往往效率低下且容易遗漏重要场景。测试工程师需要反复阅读需求文档,手动提取测试要点,这个过…...
