我正在学习c ++和java中的正则表达式。 所以我对c ++ 11正则表达式和java正则表达式进行了性能测试,表达式相同且输入相同。 奇怪的是,java正则表达式比c ++ 11正则表达式更快。 我的代码有什么问题吗? 请纠正我
Java代码:
import java.util.regex.*;
public class Main {
private final static int MAX = 1_000_000;
public static void main(String[] args) {
long start = System.currentTimeMillis();
Pattern p = Pattern.compile("^[\\w._]+@\\w+\\.[a-zA-Z]+$");
for (int i = 0; i < MAX; i++) {
p.matcher("abcd_ed123.t12y@haha.com").matches();
}
long end = System.currentTimeMillis();
System.out.print(end-start);
}
}
C ++代码:
#include
#include
#include
using namespace std;
int main()
{
long long start = GetTickCount64();
regex pat("^[\\w._]+@\\w+\\.[a-zA-Z]+$");
for (long i = 0; i < 1000000; i++) {
regex_match("abcd_ed123.t12y@haha.com", pat);
}
long long end = GetTickCount64();
cout << end - start;
return 0;
}
性能:
Java -> 1003ms
C++ -> 124360ms
明显的问题:你是如何编译c ++代码的?你有优化吗?
所以你得出的结论是你的代码有问题,因为Java比C ++表现更好?
请确认您在启用优化的情况下保存了C ++代码。此外,您不仅仅测量正则表达式,还要测量打印语句。我不认为这是一个有效的测试。
我认为这是因为std::regex没有预编译,在运行时解析正则表达式和要检查的表达式
尝试不打印结果以获得正则表达式运行时的真实值。此外,正在使用C ++编译的正则表达式?
你是怎么衡量的?我觉得你引用的数字主要反映了你的代码需要在控制台上打印一些东西的时间,而正则表达式所需的时间只是一些噪音。
@ user463035818"测量代码"在问题中......
@Borgleader ups,那我的感觉是对的;)
我使用visual studio17来编译c ++
你有时间进行调试或发布构建吗?它们之间可能存在显着的性能差异(Release允许优化)。
对!在发布版本中,VS2017的版本为1672ms。
我想我的VS构建设置有问题。我应该改变什么?或者你可以告诉我命令行编译代码。
如果你给出一个解决方案,那将是不错的选择。
在发布版本中,我没有得到GCC的输出。如果它有帮助:这是LD_PRELOAD=/home/sehe/Projects/stackoverflow/fcml-1.1.3/example/hsdis/.libs/libhsdis-amd64.so ./jre1.8.0_171/bin/java -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly Main 2>&1 > disasm.a的热点JVM组件输出
这将取决于正则表达式引擎。例如GCC中的那个不是最快的。尝试使用PCRE(pcrecpp),它应该会更快。
在我的测试中,GCC正则表达式比Java低20%,但使用PCRE正则表达式比Java测试快3倍。
MSVC正则表达式的实现非常慢,需要进行大修。更好的比较是boost :: regex。
相关stackoverflow.com/a/14229152/332733
使C ++示例可移植:
#include
#include
#include
using C = std::chrono::high_resolution_clock;
using namespace std::chrono_literals;
int main()
{
auto start = C::now();
std::regex pat("^[\\w._]+@\\w+\\.[a-zA-Z]+$");
for (long i = 0; i < 1000000; i++) {
regex_match("abcd_ed123.t12y@haha.com", pat);
}
std::cout << (C::now() - start)/1.0ms;
}
在linux上,用clang++ -std=c++14 -march=native -O3 -o clang ./test.cpp我得到595.970 ms。另见Live On Wandbox
java在561 ms中运行,在同一台机器上运行。
Update: Boost Regex runs much faster, see below comparative benchmark
Caveat: synthetic benchmarks like these are very prone to error: the compiler might sense that no observable side effects are done, and optimize the whole loop out, just to give an example.
更有趣:为混音增添乐趣
使用Boost 1.67和Nonius Micro-Benchmarking Framework
我们可以看到Boost的Regex实现速度要快得多。
在线查看详细的交互式示例数据:https://plot.ly/~sehe/25/
Code Used
#include
#include
#include
#include
#define NONIUS_RUNNER
#include
#include
template
void test(Re const& re) {
regex_match("abcd_ed123.t12y@haha.com", re);
}
static const std::regex std_normal("^[\\w._]+@\\w+\\.[a-zA-Z]+$");
static const std::regex std_optimized("^[\\w._]+@\\w+\\.[a-zA-Z]+$", std::regex::ECMAScript | std::regex::optimize);
static const boost::regex boost_normal("^[\\w._]+@\\w+\\.[a-zA-Z]+$");
static const boost::regex boost_optimized("^[\\w._]+@\\w+\\.[a-zA-Z]+$", static_cast<:regex::flag_type>(boost::regex::ECMAScript | boost::regex::optimize));
static const auto boost_xpressive = []{
using namespace boost::xpressive;
return cregex { bos >> +(_w | '.' | '_') >> '@' >> +_w >> '.' >> +alpha >> eos };
}();
NONIUS_BENCHMARK("std_normal", [] { test(std_normal); })
NONIUS_BENCHMARK("std_optimized", [] { test(std_optimized); })
NONIUS_BENCHMARK("boost_normal", [] { test(boost_normal); })
NONIUS_BENCHMARK("boost_optimized", [] { test(boost_optimized); })
NONIUS_BENCHMARK("boost_xpressive", [] { test(boost_xpressive); })
Note Here's the output of the Hotspot JVM JIT compiler:
http://stackoverflow-sehe.s3.amazonaws.com/fea76143-b712-4df9-97c3-4725b2f9e695/disasm.a.xz
This was generated using
LD_PRELOAD=/home/sehe/Projects/stackoverflow/fcml-1.1.3/example/hsdis/.libs/libhsdis-amd64.so ./jre1.8.0_171/bin/java -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly Main 2>&1 > disasm.a
尝试将std::regex::optimize传递给构造函数以强制创建FSA(理论上)
这似乎是一个bug ...
@Mgetz optimize使引擎生成DFA(确定性有限自动机)而不是NFA(非确定性有限自动机)。它们都是有限状态自动机,但是DFA需要更多的努力来生成(尽管它会运行得更快并且不能代表所有正则表达式)。
@Mgetz或optimize做了比我们预期更多的事情。添加clang + libc ++。另外,为了好玩:使用GCC的比较基准; libstdc ++; Boost1.67:plot.ly/~sehe/21(基准代码)
在混合中添加了Boost Xpressive静态正则表达式。基准测试仍然只有<30 LoC,结果显然仍然偏向于Boost Regex
@rustyx这是一个很好的电话。添加ECMAScript | optimized使得优化的regexen稍微快一些 - 更有意义。更新了答案(再次)。
只是添加已经提供的答案......
是的,C ++ 11 std::regex比Java版略慢(即使在发布模式下)。
但是使用JIT的PCRE2快3倍:
#include
#include
#define PCRE2_STATIC
#define PCRE2_CODE_UNIT_WIDTH 8
#include"pcre2.h"
using namespace std;
using namespace std::chrono;
using namespace std::chrono_literals;
int main()
{
auto start = high_resolution_clock::now();
int errn;
PCRE2_SIZE erroffset;
auto pattern = (PCRE2_SPTR8)"^[\\w._]+@\\w+\\.[a-zA-Z]+$";
pcre2_code* re = pcre2_compile(pattern, PCRE2_ZERO_TERMINATED, 0, &errn, &erroffset, nullptr);
if (!re)
cerr <
";
pcre2_match_data* match_data = pcre2_match_data_create_from_pattern(re, nullptr);
for (long i = 0; i < 1000000; i++) {
auto text = (PCRE2_SPTR8)"abcd_ed123.t12y@haha.com";
int rc = pcre2_match(re, text, PCRE2_ZERO_TERMINATED, 0, 0, match_data, nullptr);
if (rc <= 0)
cerr <
";
}
auto end = high_resolution_clock::now();
cout << (end - start) / 1ms <
";
return 0;
}
结果:
PCRE2 v10.21:139ms
Java:440ms
"JIT"意味着PCRE会直接编译成机器指令吗?从我所看到的正则表达式通常编译成DFA,引擎被高度优化,但显然是提前编译的代码。 +1表示数据点
@sehe yes,它直接编译成机器指令。有点难以击败
TIL。很棒的图书馆。好的补充!
正如各种评论者所指出的那样,您似乎正在以debug模式编译C ++代码,这会关闭许多编译器优化并为您的程序添加一些额外的诊断代码。
由于您使用的是Visual Studio 2017,请查找"解决方案配置"下拉列表并将其从debug更改为Release。
谢谢弗兰克。我对cpp的表现时间减少到1892ms。