正则表达式引擎比较(翻译自: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]shing
  • Huck[a-zA-Z]+|Saw[a-zA-Z]+
  • \b\w+nn\b
  • [a-q][^u-z]{13}x
  • Tom|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个主要的峰值,即正则表达式库性能不佳的表达式。这些表达式是:

  1. [a-q][^u-z]{13}x
  2. ∞|✓
  3. (?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子目录。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/124654.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

优化改进YOLOv5算法:加入SPD-Conv模块,让小目标无处遁形——(超详细)

1 SPD-Conv模块 论文:https://arxiv.org/pdf/2208.03641v1.pdf 摘要:卷积神经网络(CNNs)在计算即使觉任务中如图像分类和目标检测等取得了显著的成功。然而,当图像分辨率较低或物体较小时,它们的性能会灾难性下降。这是由于现有CNN常见的设计体系结构中有缺陷,即使用卷积…

UE5实现相机水平矫正

UE5实现相机水平矫正 思路,用HIT获得基于相机视角的 离散采样点,然后根据距离相机距离进行权重分析。 距离越近,采样约中心,即越接近人眼注意点,最后算出加权平均高度,赋予给相机,相机将水平旋…

聚观早报 |2024款飞凡R7官宣;小米14新配色材质

【聚观365】10月27日消息 2024款飞凡R7官宣 小米14新配色材质 金山办公2023第三季度业绩 IBM2023第三季度业绩 新东方2024财年第一季度业绩 2024款飞凡R7官宣 飞凡汽车官宣,2024款飞凡R7将于11月上市,新车将搭载飞凡巴赫座舱,同时超过1…

【Linux】——使用yum进行软件安装和卸载Win和Linux文件交互

个人主页点击直达:小白不是程序媛 Linux系列专栏:Linux被操作记 目录 前言: Linux软件包管理器yum 什么是软件包 ​编辑软件查找 如何安装软件 如何卸载软件 lrzsz的使用 将Windows的文件传送到Linux 将Linux的文件传送到Windows …

安装Pytorch的详细步骤

若为安装Anaconda的友友,可以移步到这篇文章,先安装anaconda: https://blog.csdn.net/mariodf/article/details/134119941 一、anaconda创建虚拟环境 1、首先输入: conda env list 可以查看当前的虚拟环境: 2、创…

LeetCode169——多数元素(众数)

LeetCode169——多数元素(众数) 给定一个大小为 n 的数组 nums ,返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。 你可以假设数组是非空的,并且给定的数组总是存在多数元素。 Result01&#xff08…

从0到1之微信小程序快速入门(02)

目录 页面导航 - 声明式导航 1. 导航到 tabBar 页面 2. 导航到非 tabBar 页面 3. 后退导航 ​编辑 页面导航 - 编程式导航 页面导航 - 导航传参 页面事件 - 下拉刷新事件 监听下拉刷新事件 停止下拉刷新的效果 页面事件 - 上拉触底事件 监听页面的上拉触底事件 配置…

初学编程入门基础教学视频,中文编程开发语言工具箱之豪华编辑构件,免费版中文编程软件下载

初学编程入门基础教学视频,中文编程开发语言工具箱之豪华编辑构件,免费版中文编程软件下载 构件的其中一个属性、方法,查找内容,替换内容。 构件工具箱非常丰富,其中该构件在 文本件构件板菜单下。 编程系统化课程总目…

Xilinx 7 系列 1.8V LVDS 和 2.5V LVDS 信号之间的 LVDS 兼容性

如果通过LVDS进行接口,可以按照以程图中的步骤操作,以确保满足正确使用LVDS的所有要求。 40191 - 7 系列 - 1.8V LVDS 和 2.5V LVDS 信号之间的 LVDS 兼容性 与LVDS兼容驱动器和接收器连接时,7系列LVDS和LVDS_25输入和输出应该不存在兼容性问…

卷积总结篇(普通卷积、转置卷积、膨胀卷积、分组卷积和深度可分离卷积)

目录 一、普通卷积:(“卷积”就是“加权求和”) 1.以2D卷积为例,2D卷积是一个相当简单的操作 2.卷积后的尺寸大小转换公式 3.功能 4.各个指标比较(参数量、计算量、感受野) 5.代码实现 二、转置卷积…

[架构之路-246/创业之路-77]:目标系统 - 纵向分层 - 企业信息化的呈现形态:常见企业信息化软件系统 - 客户关系管理系统CRM

目录 前言: 一、企业信息化的结果:常见企业信息化软件 1.1 客户关系管理系统CRM 1.1.1 什么是客户关系管理系统 1.1.2 CRM总体架构 1.1.3 什么类型的企业需要CRM 1.1.4 创业公司在什么阶段需要CRM 1.1.5 研发型创业公司什么时候需要CRM 1.1.6 C…

【OpenCV实现图像找到轮廓的不同特征,就像面积,周长,质心,边界框等等。】

文章目录 概要图像矩凸包边界矩形 概要 OpenCV是一个流行的计算机视觉库,它提供了许多图像处理和分析功能,其中包括查找图像中物体的轮廓。通过查找轮廓,可以提取许多有用的特征,如面积、周长、质心、边界框等。 以下是几种使用…

Docker:安装MySQL

Docker:安装MySQL 1. 部署MySQL2.部署多个MySQL服务 1. 部署MySQL 首先需要安装Docker,安装Docker地址:http://t.csdnimg.cn/utPGF 安装命令: docker run -d \--name mysql \-p 3306:3306 \-e TZAsia/Shanghai \-e MYSQL_ROOT…

DIY相机(一)libcamera库

相机选型 DIY相机首先是要确定使用的相机型号。兼容树莓派,画质好一些的,目前主要有两款:一是Raspberry Pi Camera Module 3,二是Raspberry Pi HQ Camera。 下图是Raspberry Pi Camera Module 3的相关特性。支持自动对焦和HDR等…

python随手小练14

题目: 文件操作 : 根据文件要求(测试)筛选出数据并且放入一个新的文件 具体操作: f1 open("1.txt","r",encoding"UTF-8") f2 open("2.txt","w",encoding"U…

软考系统架构师知识点集锦八:嵌入式系统

一、考情分析 二、考点精讲 2.1嵌入式系统概述 2.1.1基本概念 (1)嵌入式系统是以应用为中心、以计算机技术为基础,并将可配置与可裁剪的软、硬件集成于一体的专用计算机系统,需要满足应用对功能、可靠性、成本、体积和功耗等方面的严格要求。 (2)从计算机角度看,嵌…

轻量封装WebGPU渲染系统示例<8>- 渲染器基本场景管理(源码)

当前示例源码github地址: https://github.com/vilyLei/voxwebgpu/blob/main/src/voxgpu/sample/RSceneTest.ts 此示例渲染系统实现的特性: 1. 用户态与系统态隔离。 2. 高频调用与低频调用隔离。 3. 面向用户的易用性封装。 4. 渲染数据和渲染机制分离。 5. 用户操作和渲…

数据结构:算法(特性,时间复杂度,空间复杂度)

目录 1.算法的概念2.算法的特性1.有穷性2.确定性3.可行性4.输入5.输出 3.好算法的特质1.正确性2.可读性3.健壮性4.高效率与低存储需求 4.算法的时间复杂度1.事后统计的问题2.复杂度表示的计算1.加法规则2.乘法规则3.常见函数数量级比较 5.算法的空间复杂度1.程序的内存需求2.例…

SpringBoot通过注解形式实现系统操作日志

介绍 我们在日常开发工作中,肯定逃不开与日志接触,一些比较严谨的后台管理系统里面会涉及到一些比较重要的资料,有些公司为了知道有哪些人登录了系统,是谁在什么时候修改了用户信息或者资料,所以就有了操作日志这么个…

1.4 安全服务

思维导图: 1.4 安全服务 定义:在通信开放系统中,为系统或数据传输提供足够安全的协议层服务。 RFC4949 定义:由系统提供的对系统资源进行特殊保护的处理或通信服务。安全服务通过安全机制来实现安全策略。 分类:X.800 …