Perl 6 中的正则表达式速度
Regex speed in Perl 6
我以前只使用 bash
正则表达式、grep
、sed
、awk
等。在尝试 Perl 6
regexes
我的印象是它们的工作速度比我预期的要慢,但可能的原因是我对它们的处理不正确。
我做了一个简单的测试来比较 Perl 6
和 bash
中的类似操作。这是 Perl 6
代码:
my @array = "aaaaa" .. "fffff";
say +@array; # 7776 = 6 ** 5
my @search = <abcde cdeff fabcd>;
my token search {
@search
}
my @new_array = @array.grep({/ <search> /});
say @new_array;
然后我将 @array
打印到一个名为 array
的文件中(7776 行),制作了一个名为 search
的文件,其中包含 3 行(abcde
、cdeff
, fabcd
) 并进行了简单的 grep
搜索。
$ grep -f search array
在两个程序产生相同结果后,正如预期的那样,我测量了它们工作的时间。
$ time perl6 search.p6
real 0m6,683s
user 0m6,724s
sys 0m0,044s
$ time grep -f search array
real 0m0,009s
user 0m0,008s
sys 0m0,000s
那么,我的 Perl 6 代码哪里做错了?
UPD: 如果我将搜索标记传递给 grep
,遍历 @search
数组,程序运行得更快:
my @array = "aaaaa" .. "fffff";
say +@array;
my @search = <abcde cdeff fabcd>;
for @search -> $token {
say ~@array.grep({/$token/});
}
$ time perl6 search.p6
real 0m1,378s
user 0m1,400s
sys 0m0,052s
如果我手动定义每个搜索模式,它的工作速度会更快:
my @array = "aaaaa" .. "fffff";
say +@array; # 7776 = 6 ** 5
say ~@array.grep({/abcde/});
say ~@array.grep({/cdeff/});
say ~@array.grep({/fabcd/});
$ time perl6 search.p6
real 0m0,587s
user 0m0,632s
sys 0m0,036s
grep
命令比Perl 6的正则表达式要简单得多,而且经过多年的优化。这也是 Rakudo 中没有看到太多优化的领域之一;部分原因是它被认为是一件很难做的事情。
要获得更高性能的示例,您可以预编译正则表达式:
my $search = "/@search.join('|')/".EVAL;
# $search = /abcde|cdeff|fabcd/;
say ~@array.grep($search);
该更改导致它在大约半秒内变为 运行。
如果 @search
中有任何可能的恶意数据,而您必须这样做,使用它可能更安全:
"/@search».Str».perl.join('|')/".EVAL
编译器无法完全为 /@search/
生成优化代码,因为 @search
可能会在正则表达式编译后发生变化。可能发生的情况是,第一次使用正则表达式时,它会被重新编译成更好的形式,然后只要 @search
没有被修改就将其缓存。
(我认为 Perl 5 做了类似的事情)
您必须牢记的一个重要事实是,Perl 6 中的正则表达式只是一种用领域特定子语言编写的方法。
我以前只使用 bash
正则表达式、grep
、sed
、awk
等。在尝试 Perl 6
regexes
我的印象是它们的工作速度比我预期的要慢,但可能的原因是我对它们的处理不正确。
我做了一个简单的测试来比较 Perl 6
和 bash
中的类似操作。这是 Perl 6
代码:
my @array = "aaaaa" .. "fffff";
say +@array; # 7776 = 6 ** 5
my @search = <abcde cdeff fabcd>;
my token search {
@search
}
my @new_array = @array.grep({/ <search> /});
say @new_array;
然后我将 @array
打印到一个名为 array
的文件中(7776 行),制作了一个名为 search
的文件,其中包含 3 行(abcde
、cdeff
, fabcd
) 并进行了简单的 grep
搜索。
$ grep -f search array
在两个程序产生相同结果后,正如预期的那样,我测量了它们工作的时间。
$ time perl6 search.p6
real 0m6,683s
user 0m6,724s
sys 0m0,044s
$ time grep -f search array
real 0m0,009s
user 0m0,008s
sys 0m0,000s
那么,我的 Perl 6 代码哪里做错了?
UPD: 如果我将搜索标记传递给 grep
,遍历 @search
数组,程序运行得更快:
my @array = "aaaaa" .. "fffff";
say +@array;
my @search = <abcde cdeff fabcd>;
for @search -> $token {
say ~@array.grep({/$token/});
}
$ time perl6 search.p6
real 0m1,378s
user 0m1,400s
sys 0m0,052s
如果我手动定义每个搜索模式,它的工作速度会更快:
my @array = "aaaaa" .. "fffff";
say +@array; # 7776 = 6 ** 5
say ~@array.grep({/abcde/});
say ~@array.grep({/cdeff/});
say ~@array.grep({/fabcd/});
$ time perl6 search.p6
real 0m0,587s
user 0m0,632s
sys 0m0,036s
grep
命令比Perl 6的正则表达式要简单得多,而且经过多年的优化。这也是 Rakudo 中没有看到太多优化的领域之一;部分原因是它被认为是一件很难做的事情。
要获得更高性能的示例,您可以预编译正则表达式:
my $search = "/@search.join('|')/".EVAL;
# $search = /abcde|cdeff|fabcd/;
say ~@array.grep($search);
该更改导致它在大约半秒内变为 运行。
如果 @search
中有任何可能的恶意数据,而您必须这样做,使用它可能更安全:
"/@search».Str».perl.join('|')/".EVAL
编译器无法完全为 /@search/
生成优化代码,因为 @search
可能会在正则表达式编译后发生变化。可能发生的情况是,第一次使用正则表达式时,它会被重新编译成更好的形式,然后只要 @search
没有被修改就将其缓存。
(我认为 Perl 5 做了类似的事情)
您必须牢记的一个重要事实是,Perl 6 中的正则表达式只是一种用领域特定子语言编写的方法。