捕获组VS非捕获组

问题描述 投票:0回答:3

我尝试测试正则表达式的捕获组和非捕获组的性能。 顺便说一句,捕获组和非捕获组之间存在细微差别。 这个结果正常吗?

[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
regex
3个回答
17
投票

通常,非捕获组比捕获组表现更好,因为它们需要更少的内存分配,并且不复制组匹配。然而,有三个重要的警告:

  • 对于具有短匹配的简单、简短表达式,差异通常非常小。
  • 启动像
    grep
    这样的程序的行为本身需要大量的时间和内存,并且可能会压倒使用非捕获组所获得的任何微小改进。
  • 某些语言以相同的方式实现捕获组和非捕获组,导致后者没有性能提升。

5
投票

如果使用大量的捕获组。 差别好像更大。

谢谢大家。:)

[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

0
投票

由于您的

sample.log
的内容未知,因此很难以可复制的方式进行分析。让我们做一些简单的实验:

  1. 生成 100k 行随机字符:
$ python3 -c 'for i in range(100000): print("".join(__import__("random").choices(__import__("string").ascii_lowercase, k=100)))' > /tmp/sample.log
  1. 与 grep 匹配
$ time grep -ciP '(?:get|set).*' /tmp/sample.log
1099

real    0m0.157s
user    0m0.119s
sys     0m0.038s

我注意到 user 所花费的时间变化很大,从 0.08 到 0.14 秒不等,这表明大部分时间都花在了启动 grep 程序而不是正则表达式匹配的开销上。让我们将文件大小加倍:


  1. 将文件大小乘以 64:
$ 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
  1. 与 grep 匹配:
$ time grep -ciP '(?:(?:get|set)(?:...))' /tmp/sample64.log
67840

real    0m3.874s
user    0m3.658s
sys     0m0.216s

连续运行5次,我得到:

  • 捕获:[4.128, 2.929, 3.628, 3.270, 2.862]
  • 非捕获:[3.649, 3.919, 3.035, 3.325, 2.877]

他们的平均值是 3.363 vs 3.360,与每次运行的偏差相比,这是微不足道的。也许 grep 实现非捕获组的方式与捕获组相同。让我们尝试不同的实现:


  1. 准备测试.py:
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))
  1. 运行测试:
$ python3 test.py
Capture group:          140615772 ns
Non-capture group:      132087375 ns

从结果中,我们可以得出结论:

  • grep的实现中,捕获组和非捕获组没有显着差异
  • 在 Python
    re
    实现中,更改为
    (?:)
  • 性能提升约 +6%
最新问题
© www.soinside.com 2019 - 2024. All rights reserved.