Python grep 代码比命令行的 grep 慢得多

标签 python performance grep

我只是为 approved="no" 模式搜索一些 Xliff 文件。我有一个 Shell 脚本和一个 Python 脚本,性能差异很大(一组 393 个文件,总共 3,686,329 行,Shell 脚本的用户时间为 0.1s,Python 脚本的用户时间为 6.6s)。

外壳:grep 'approved="no"' FILE
python :

def grep(pattern, file_path):
    ret = False

    with codecs.open(file_path, "r", encoding="utf-8") as f:
        while 1 and not ret:
            lines = f.readlines(100000)
            if not lines:
                break
            for line in lines:
                if re.search(pattern, line):
                    ret = True
                    break
    return ret

有任何使用多平台解决方案提高性能的想法吗?

结果

以下是应用一些建议的解决方案后的一些结果。
测试在装有 Python 2.6.6 的 RHEL6 Linux 机器上运行。
工作集:393个Xliff文件,共3,686,329行。
数字是以秒为单位的用户时间。

grep_1(io,加入 100,000 行文件):50 秒
grep_3(mmap):0.7s
Shell 版本(Linux grep):0.130s

最佳答案

Python,作为一种解释型语言,与 grep 的编译 C 版本相比,它总是比较慢。

除此之外,您的 Python 实现与您的 grep 示例相同。它不返回匹配的行,它只是测试模式是否与任何一行中的字符匹配。更仔细的比较是:

grep -q 'approved="no"' FILE

一旦找到匹配项就会返回并且不会产生任何输出。

您可以通过更有效地编写 grep() 函数来显着加快代码速度:

def grep_1(pattern, file_path):
    with io.open(file_path, "r", encoding="utf-8") as f:
        while True:
            lines = f.readlines(100000)
            if not lines:
                return False
            if re.search(pattern, ''.join(lines)):
                return True

这使用 io 而不是 codecs 我发现后者更快一些。 while 循环条件不需要检查 ret 并且您可以在知道结果后立即从函数返回。无需为每个单独的 ilne 运行 re.search() - 只需加入行并执行单个搜索。

以内存使用为代价,你可以试试这个:

import io

def grep_2(pattern, file_path):
    with io.open(file_path, "r", encoding="utf-8") as f:
        return re.search(pattern, f.read())

如果内存有问题,您可以mmap 文件并在mmap 上运行正则表达式搜索:

import io
import mmap

def grep_3(pattern, file_path):
    with io.open(file_path, "r", encoding="utf-8") as f:
        return re.search(pattern, mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ))

mmap 将有效地以页为单位从文件中读取数据,而不会消耗大量内存。此外,您可能会发现 mmap 比其他解决方案运行得更快。


对每个函数使用 timeit 表明是这种情况:

10 loops, best of 3: 639 msec per loop       # grep()
10 loops, best of 3: 78.7 msec per loop      # grep_1()
10 loops, best of 3: 19.4 msec per loop      # grep_2()
100 loops, best of 3: 5.32 msec per loop     # grep_3()

The file was /usr/share/dict/words containing approx 480,000 lines and the search pattern was zymurgies, which occurs near the end of the file. For comparison, when pattern is near the start of the file, e.g. abaciscus, the times are:

10 loops, best of 3: 62.6 msec per loop       # grep()
1000 loops, best of 3: 1.6 msec per loop      # grep_1()
100 loops, best of 3: 14.2 msec per loop      # grep_2()
10000 loops, best of 3: 37.2 usec per loop    # grep_3()

which again shows that the mmap version is fastest.


Now comparing the grep command with the Python mmap version:

$ time grep -q zymurgies /usr/share/dict/words

real    0m0.010s
user    0m0.007s
sys 0m0.003s

$ time python x.py grep_3    # uses mmap

real    0m0.023s
user    0m0.019s
sys 0m0.004s

考虑到 grep 的优势,这还算不错。

关于Python grep 代码比命令行的 grep 慢得多,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38916645/

相关文章:

java - 使用 Java 代理和 byte-buddy 测量执行时间

python - 为什么 "any"在 python 中的 bool 值上有时比 "max"运行得快得多,有时却慢得多?

linux - 尝试使用随机结尾的 grep url

linux - ps | 之间的区别grep safe 和 ps | grep 安全\$

python - 对嵌套元组列表进行排序

python - 存储固定 key key :value datasets that are accessed by keys in python?的最佳方式

python - 如何在python中存储绝对路径并将其显示为目录结构?

python - 在反向代理后面添加前缀 url_for()

mysql - 导入MySQL数据库很慢,但没有明显的瓶颈

regex - 跨多行的复杂匹配