我尝试测试正则表达式的捕获组和非捕获组的性能。 顺便说一句,捕获组和非捕获组之间存在细微差别。 这个结果正常吗?
[root@Sensor ~]# ll -h sample.log
-rw-r--r-- 1 root root 21M Oct 20 23:01 sample.log
[root@Sensor ~]# time grep -ciP '(get|post).*' sample.log
20000
real 0m0.083s
user 0m0.070s
sys 0m0.010s
[root@Sensor ~]# time grep -ciP '(?:get|post).*' sample.log
20000
real 0m0.083s
user 0m0.077s
sys 0m0.004s
通常,非捕获组比捕获组表现更好,因为它们需要更少的内存分配,并且不复制组匹配。然而,有三个重要的警告:
grep
这样的程序的行为本身需要大量的时间和内存,并且可能会压倒使用非捕获组所获得的任何微小改进。如果使用大量的捕获组。 差别好像更大。
谢谢大家。:)
[root@Sensor ~]# time grep -ciP "(get|post)\s[^\s]+" sample.log
20000
real 0m0.057s
user 0m0.051s
sys 0m0.005s
[root@Sensor ~]# time grep -ciP "(?:get|post)\s[^\s]+" sample.log
20000
real 0m0.061s
user 0m0.053s
sys 0m0.006s
[root@Sensor ~]# time grep -ciP "(get|post)\s[^\s]+(get|post)" sample.log
1880
real 0m0.839s
user 0m0.833s
sys 0m0.005s
[root@Sensor ~]# time grep -ciP "(?:get|post)\s[^\s]+(?:get|post)" sample.log
1880
real 0m0.744s
user 0m0.741s
sys 0m0.003s
由于您的
sample.log
的内容未知,因此很难以可复制的方式进行分析。让我们做一些简单的实验:
$ python3 -c 'for i in range(100000): print("".join(__import__("random").choices(__import__("string").ascii_lowercase, k=100)))' > /tmp/sample.log
$ time grep -ciP '(?:get|set).*' /tmp/sample.log
1099
real 0m0.157s
user 0m0.119s
sys 0m0.038s
我注意到 user 所花费的时间变化很大,从 0.08 到 0.14 秒不等,这表明大部分时间都花在了启动 grep 程序而不是正则表达式匹配的开销上。让我们将文件大小加倍:
$ cat /tmp/sample.log | sed '/.*/p' | sed '/.*/p' | sed '/.*/p' | sed '/.*/p' | sed '/.*/p' | sed '/.*/p' > /tmp/sample64.log
$ du -h /tmp/sample64.log
617M /tmp/sample64.log
$ time grep -ciP '(?:(?:get|set)(?:...))' /tmp/sample64.log
67840
real 0m3.874s
user 0m3.658s
sys 0m0.216s
连续运行5次,我得到:
他们的平均值是 3.363 vs 3.360,与每次运行的偏差相比,这是微不足道的。也许 grep 实现非捕获组的方式与捕获组相同。让我们尝试不同的实现:
from string import ascii_lowercase
from time import monotonic_ns
import re
from random import choices
# prepare content
lines = []
for i in range(100000):
lines.append(''.join(choices(ascii_lowercase, k=100)))
cap_re = re.compile(r'((get|set)(...))')
noncap_re = re.compile(r'(?:(?:get|set)(?:...))')
c = 0
# test capture group
start = monotonic_ns()
for ln in lines:
if cap_re.search(ln):
c += 1
print('Capture group: \t{} ns'.format(monotonic_ns() - start))
# test non capture group
start = monotonic_ns()
for ln in lines:
if noncap_re.search(ln):
c += 1
print('Non-capture group:\t{} ns'.format(monotonic_ns() - start))
$ python3 test.py
Capture group: 140615772 ns
Non-capture group: 132087375 ns
从结果中,我们可以得出结论:
re
实现中,更改为 (?:)