C++与.NET正则表达式性能
C++ vs .NET regex performance
在Konrad Rudolph对相关问题的评论的提示下,我编写了以下程序来测试F#中的正则表达式性能:
open System.Text.RegularExpressions
let str = System.IO.File.ReadAllText "C:\Users\Jon\Documents\pg10.txt"
let re = System.IO.File.ReadAllText "C:\Users\Jon\Documents\re.txt"
for _ in 1..3 do
let timer = System.Diagnostics.Stopwatch.StartNew()
let re = Regex(re, RegexOptions.Compiled)
let res = Array.Parallel.init 4 (fun _ -> re.Split str |> Seq.sumBy (fun m -> m.Length))
printfn "%A %fs" res timer.Elapsed.TotalSeconds
和等效的C++:
#include "stdafx.h"
#include <windows.h>
#include <regex>
#include <vector>
#include <string>
#include <fstream>
#include <cstdio>
#include <codecvt>
using namespace std;
wstring load(wstring filename) {
const locale empty_locale = locale::empty();
typedef codecvt_utf8<wchar_t> converter_type;
const converter_type* converter = new converter_type;
const locale utf8_locale = locale(empty_locale, converter);
wifstream in(filename);
wstring contents;
if (in)
{
in.seekg(0, ios::end);
contents.resize(in.tellg());
in.seekg(0, ios::beg);
in.read(&contents[0], contents.size());
in.close();
}
return(contents);
}
int count(const wstring &re, const wstring &s){
static const wregex rsplit(re);
auto rit = wsregex_token_iterator(s.begin(), s.end(), rsplit, -1);
auto rend = wsregex_token_iterator();
int count=0;
for (auto it=rit; it!=rend; ++it)
count += it->length();
return count;
}
int _tmain(int argc, _TCHAR* argv[])
{
wstring str = load(L"pg10.txt");
wstring re = load(L"re.txt");
__int64 freq, tStart, tStop;
unsigned long TimeDiff;
QueryPerformanceFrequency((LARGE_INTEGER *)&freq);
QueryPerformanceCounter((LARGE_INTEGER *)&tStart);
vector<int> res(4);
#pragma omp parallel num_threads(4)
for(auto i=0; i<res.size(); ++i)
res[i] = count(re, str);
QueryPerformanceCounter((LARGE_INTEGER *)&tStop);
TimeDiff = (unsigned long)(((tStop - tStart) * 1000000) / freq);
printf("(%d, %d, %d, %d) %fsn", res[0], res[1], res[2], res[3], TimeDiff/1e6);
return 0;
}
两个程序都加载两个文件作为 unicode 字符串(我使用的是圣经的副本),构造一个非平凡的 unicode 正则表达式w?w?w?w?w?w
并使用正则表达式并行拆分字符串四次,返回拆分字符串长度的总和(以避免分配)。
面向 64 位的发布版本中,在 Visual Studio 中运行(为 C++ 启用了 MP 和 OpenMP),C++需要 43.5 秒,F# 需要 3.28 秒(快 13 倍以上)。这并不让我感到惊讶,因为我相信 .NET JIT 将正则表达式编译为本机代码,而 stdlib C++解释它,但我想要一些同行评审。
我的C++代码中是否存在性能错误,或者这是编译正则表达式与解释正则表达式的结果?
编辑:Billy ONeal指出.NET可以对w
有不同的解释,所以我在一个新的正则表达式中明确表示:
[0-9A-Za-z_]?[0-9A-Za-z_]?[0-9A-Za-z_]?[0-9A-Za-z_]?[0-9A-Za-z_]?[0-9A-Za-z_]
这实际上使 .NET 代码大大加快(C++相同),将 F# 的时间从 3.28 秒缩短到 2.38 秒(快 17 倍以上)。
这些基准测试并不是真正可比的——C++和.NET实现了完全不同的正则表达式语言(ECMAScript与Perl),并且由完全不同的正则表达式引擎提供支持。 .NET(据我所知)受益于这里的GRETA项目,该项目产生了一个绝对出色的正则表达式引擎,该引擎已经调整了多年。相比之下,C++ std::regex
是最近添加的(至少在 MSVC++ 上,我假设您正在使用非标准类型__int64
和朋友)。
您可以在此处看到 GRETA 与更成熟的std::regex
实现相比boost::regex
(尽管该测试是在 Visual Studio 2003 上完成的)。
您还应该记住,正则表达式性能高度依赖于源字符串和正则表达式。一些正则表达式引擎花费大量时间来解析正则表达式以更快地浏览更多源文本;这种权衡只有在解析大量文本时才有意义。一些正则表达式引擎牺牲了扫描速度,以相对昂贵地进行匹配(因此匹配的数量会产生影响)。这里有大量的权衡;一对输入确实会给故事蒙上阴影。
因此,为了更明确地回答您的问题:这种变化在正则表达式引擎中是正常的,无论是编译还是解释。看看上面的boost测试,通常最快和最慢的实现之间的差异相差数百倍 - 17x并不是那么奇怪,这取决于你的用例。
- 使用正则表达式regex_search在字符串中查找字符串
- 在 C++ 中使用正则表达式错误时出现问题 括号表达式中的范围无效
- C++正则表达式无限循环
- FindPackageHandleStandardArgs.cmake:137 的 CMake 错误(消息):找不到 Boost (缺少:正则表达式)(找到合适的版本"1.72.0",
- 定义有趣的宏和正则表达式在Z3 C++绑定
- 带有多个字符分隔符的正则表达式
- C++ 使用增强正则表达式库时断言崩溃
- 有人可以帮助我处理正则表达式吗?
- 使用正则表达式获取大括号块的列表
- 正则表达式以匹配数字的重复模式,后跟任何类型的分隔符?
- 组合正则表达式部分使用 | 不起作用的 C++
- 为什么C++正则表达式这么慢?
- 如何使HTML5电子邮件验证正则表达式在C++中工作?
- 在C++中实现正则表达式
- C++正则表达式替换整个单词
- 用C++编写正则表达式的正确方法是什么?
- 如何从Stroustrup的C++书中解释这个正则表达式?
- C++与.NET正则表达式性能
- 提升::正则表达式性能问题
- 提高c++正则表达式替换性能