当前位置: 首页 > news >正文

正则表达式引擎比较(翻译自: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子目录。

相关文章:

正则表达式引擎比较(翻译自: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 &…...

使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装

以下是基于 vant-ui&#xff08;适配 Vue2 版本 &#xff09;实现截图中照片上传预览、删除功能&#xff0c;并封装成可复用组件的完整代码&#xff0c;包含样式和逻辑实现&#xff0c;可直接在 Vue2 项目中使用&#xff1a; 1. 封装的图片上传组件 ImageUploader.vue <te…...

从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)

设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile&#xff0c;新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...

工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配

AI3D视觉的工业赋能者 迁移科技成立于2017年&#xff0c;作为行业领先的3D工业相机及视觉系统供应商&#xff0c;累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成&#xff0c;通过稳定、易用、高回报的AI3D视觉系统&#xff0c;为汽车、新能源、金属制造等行…...

vue3+vite项目中使用.env文件环境变量方法

vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量&#xff0c;这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...

DeepSeek源码深度解析 × 华为仓颉语言编程精粹——从MoE架构到全场景开发生态

前言 在人工智能技术飞速发展的今天&#xff0c;深度学习与大模型技术已成为推动行业变革的核心驱动力&#xff0c;而高效、灵活的开发工具与编程语言则为技术创新提供了重要支撑。本书以两大前沿技术领域为核心&#xff0c;系统性地呈现了两部深度技术著作的精华&#xff1a;…...

【FTP】ftp文件传输会丢包吗?批量几百个文件传输,有一些文件没有传输完整,如何解决?

FTP&#xff08;File Transfer Protocol&#xff09;本身是一个基于 TCP 的协议&#xff0c;理论上不会丢包。但 FTP 文件传输过程中仍可能出现文件不完整、丢失或损坏的情况&#xff0c;主要原因包括&#xff1a; ✅ 一、FTP传输可能“丢包”或文件不完整的原因 原因描述网络…...

Python 高级应用10:在python 大型项目中 FastAPI 和 Django 的相互配合

无论是python&#xff0c;或者java 的大型项目中&#xff0c;都会涉及到 自身平台微服务之间的相互调用&#xff0c;以及和第三发平台的 接口对接&#xff0c;那在python 中是怎么实现的呢&#xff1f; 在 Python Web 开发中&#xff0c;FastAPI 和 Django 是两个重要但定位不…...

java+webstock

maven依赖 <dependency><groupId>org.java-websocket</groupId><artifactId>Java-WebSocket</artifactId><version>1.3.5</version></dependency><dependency><groupId>org.apache.tomcat.websocket</groupId&…...

Electron简介(附电子书学习资料)

一、什么是Electron&#xff1f; Electron 是一个由 GitHub 开发的 开源框架&#xff0c;允许开发者使用 Web技术&#xff08;HTML、CSS、JavaScript&#xff09; 构建跨平台的桌面应用程序&#xff08;Windows、macOS、Linux&#xff09;。它将 Chromium浏览器内核 和 Node.j…...

【RabbitMQ】- Channel和Delivery Tag机制

在 RabbitMQ 的消费者代码中&#xff0c;Channel 和 tag 参数的存在是为了实现消息确认机制&#xff08;Acknowledgment&#xff09;和精细化的消息控制。 Channel 参数 作用 Channel 是 AMQP 协议的核心操作接口&#xff0c;通过它可以直接与 RabbitMQ 交互&#xff1a; 手…...