内置的grep比Linux自带的grep慢

7
我正在尝试理解为什么我自己构建的grep比系统自带的要慢得多,并试图找出系统自带的grep使用了哪些编译器选项。
操作系统版本:CentOS release 5.3 (Final) 系统自带的grep:
版本:grep (GNU grep) 2.5.1 大小:88896字节 ldd输出: libpcre.so.0 => /lib64/libpcre.so.0 (0x0000003991800000) libc.so.6 => /lib64/libc.so.6 (0x0000003985a00000) /lib64/ld-linux-x86-64.so.2 (0x0000003984a00000)
我构建的grep:
版本:2.5.1 大小:256437字节 ldd输出: libpcre.so.0 => /lib64/libpcre.so.0 (0x0000003991800000) libc.so.6 => /lib64/libc.so.6 (0x0000003985a00000) /lib64/ld-linux-x86-64.so.2 (0x0000003984a00000)
当在一个大型文本文件中运行正则表达式搜索时,系统grep(330毫秒)的性能比我构建的grep(22430毫秒)要快得多。
以下是我用来计时的命令:
%time src/grep ".*asa.*" large_list.txt > /dev/null real 0m22.430s user 0m22.291s sys 0m0.080s
或者
%time bin/grep ".*asa.*" large_list.txt > /dev/null real 0m0.331s user 0m0.236s sys 0m0.081s
系统grep显然使用了一些优化选项,这导致了巨大的性能差异。
有人可以帮我看看系统grep可能使用了哪些选项吗?
以下是我构建时一个源文件的编译选项:
gcc -DLIBDIR=\"/usr/local/lib\" -DHAVE_CONFIG_H -I. -I.. -I.. -I. -I../intl -g -O2 -MT xstrtol.o -MD -MP -MF .deps/xstrtol.Tpo -c -o xstrtol.o xstrtol.c
./configure的输出:
检查是否有一个兼容BSD的安装程序... /usr/bin/install -c 检查构建环境是否健康... 是 检查是否有线程安全的mkdir -p... /bin/mkdir -p 检查gawk是否存在... gawk 检查make是否设置了$(MAKE)... 是 检查构建系统类型... x86_64-unknown-linux-gnu 检查主机系统类型... x86_64-unknown-linux-gnu 检查gawk是否存在... (已缓存) gawk 检查gcc是否存在... gcc 检查C编译器默认输出文件名... a.out 检查C编译器是否工作... 是 检查是否进行交叉编译... 否 检查可执行文件的后缀名... 检查目标文件的后缀名... o 检查是否使用GNU C编译器... 是 检查gcc是否接受-g选项... 是 检查是否需要ISO C89的选项接受... 不需要 检查makefile文件中包含的头文件风格... GNU 检查gcc的依赖关系风格... gcc3 检查是否有一个兼容BSD的安装程序... /usr/bin/install -c 检查是否有ranlib... ranlib 检查是否有getconf... getconf 检查CFLAGS值是否请求大文件支持... 检查LDFLAGS值是否请求大文件支持... 检查LIBS值是否请求大文件支持... 检查_FILE_OFFSET_BITS是否存在... 否 检查_LARGEFILE_SOURCE是否存在... 否 检查_LARGE_FILES是否存在... 否 检查函数原型... 是 检查如何运行C预处理器... gcc -E 检查能否处理长行和-e的grep... /bin/grep 检查egrep的存在... /bin/grep -E 检查ANSI C头文件的存在... 是 检查sys/types.h的存在... 是 检查sys/stat.h的存在... 是 检查stdlib.h的存在... 是 检查string.h的存在... 是 检查memory.h的存在... 是 检查strings.h的存在... 是 检查inttypes.h的存在... 是 检查stdint.h的存在... 是 检查unistd.h的存在... 是 检查string.h的存在... (已缓存) 是 检查size_t的存在... 是 检查ssize_t的存在... 是 检查是否符合ANSI C标准的const... 是 检查inttypes.h的存在... 是 检查unsigned long long的存在... 是 检查ANSI C头文件的存在... (已缓存) 是 检查string.h的存在... (已缓存) 是 检查stdlib.h的存在... (已缓存) 是 检查sys/param.h是否可用... 是 检查sys/param.h是否存在... 是 检查memory.h的存在... (已缓存) 是 检查unistd.h的存在... (已缓存) 是 检查libintl.h是否可用... 是 检查libintl.h是否存在... 是 检查wctype.h是否可用... 是 检查wctype.h是否存在... 是 检查wchar.h是否可用... 是 检查wchar.h是否存在... 是 检查定义DIR的dirent.h是否存在... 是 检查包含opendir的库是否存在... 不需要 检查stat文件模式宏是否损坏... 否 检查是否具有工作的alloca.h... 是 检查是否具有alloca函数... 是 检查closedir是否返回void... 否 检查stdlib.h的存在... (已缓存) 是 检查unistd.h的存在... (已缓存) 是 检查是否具有getpagesize... 是 检查是否具有工作的mmap... 是 检查btowc是否存在... 是 检查isascii是否存在... 是 检查iswctype是否存在... 是 检查mbrlen是否存在... 是 检查memmove是否存在... 是 检查setmode是否存在... 否 检查strerror是否存在... 是 检查wcrtomb是否存在... 是 检查wcscoll是否存在... 是 检查wctype是否存在... 是 检
谢谢,Kumar。
5个回答

10

为什么不直接获取CentOS的grep二进制文件的SRPM并将其编译选项与您的进行比较?我猜这比让整个StackOverflow社区盲目地摸索要好得多。

编辑:您是否使用具有多字节编码的区域设置?(注:如果您不知道这是什么意思,那么答案可能是“是”,因为UTF-8已经成为大多数Linux发行版的默认编码方式,事实上RedHat(以及CentOS)是最早转换的。

在这种情况下,GNU grep非常慢。而且这不仅适用于GNU grep,还适用于几乎所有进行某种文本处理的GNU工具。FSF拒绝接受任何改进多字节性能的补丁,除非这些补丁被证明不会降低固定宽度编码的速度。然而,由于任何改进多字节编码性能的补丁都必须至少包含一些if语句,因此实际上不可能编写一个不至少通过该if语句的开销降低固定宽度编码速度的补丁。因此,GNU工具的UTF-8性能将会持续低劣。

无论如何,大多数Linux发行版都不在乎自由软件基金会的想法,他们仍然会打补丁来修改GNU grep。Fedora Rawhide SRPM 包含了一个名为 grep-2.5.3-egf-speedup.patch 的补丁,它能将GNU grep的UTF-8性能提高几个数量级。(由于这个补丁已经是从2005年开始使用的,我认为它也被 CentOS 所使用。)这个补丁同样被 Mac OSX、Debian、Ubuntu等所使用,实际上,几乎没有人使用纯粹的GNU grep。在多字节编码下进行文本处理永远不会像在固定宽度编码下那么快,但至少应该是可以相当的,而不是比它慢50倍(甚至有人报告说是1500倍)。
还有另一个名为 dfa-optional 的补丁,它使得grep只是简单地使用GNU libc的正则表达式引擎,而不是自己的引擎。这个引擎在处理UTF-8时不仅要快得多,而且还有更少的错误。
因此,你可能需要使用 export LC_ALL=POSIX 重新运行你的基准测试。如果这样解决了你的问题,你需要应用上述两个补丁中的任意一个。
这两个RedHat bug报告中也提供了更多信息:

故事的寓意:尽管有一种普遍的看法,即Linux发行版有时确实知道自己在做什么。不要怀疑他们。


感谢您的优秀建议。我尝试从srpm构建,但与系统中的grep二进制文件或rpm相比,性能仍然很慢。也许需要在特定环境下构建才能获得正确的配置?我已经在其中一个CentOS论坛上发布了问题。我在其他Linux版本上也看到了相同的行为。 - kumar

5

你使用了-O2标志进行编译。 为什么没用-O3标志。请参阅此处,了解gcc可用的优化选项的说明。

使用英特尔的ICC编译器也可以帮助提高性能,但这确实取决于应用程序。 同时,它不是免费的。

编辑,我刚看到你的编译行中有-g标志。将其删除,因为它会开启调试模式,这会导致严重的性能损失。


1
几乎与O3的时间相同.. 实际0m22.271秒 用户0m22.163秒 系统0m0.079秒使用-O3的grep大小为344944字节 - kumar

2
除了 -O 选项之外,还有一件需要注意的事情是,看起来你正在使用调试符号 "-g" 进行构建。
调试通常会增加二进制文件大小,并可能降低二进制文件的性能。我想 grep 已经非常稳定了,你不需要为它添加调试符号。

去掉-g选项后,大小确实减小到101733(使用-O2),但时间仍然相同.. 真实时间0m22.358秒 用户时间0m22.231秒 系统时间0m0.093秒 - kumar

1
你使用的是哪个版本的GCC?如果我没记错,GCC 4进行了重大改版,这使得一段时间内一些优化代码失效了。

0

由于性能差距如此之大,很可能是算法/代码的差异,而不仅仅是编译器优化级别的差异。是什么让你怀疑编译器呢?


我从GNU(2.5.4)获取了grep代码,与系统中的2.5.1相比,在代码上不应该有太大的变化。我没有使用任何自己的代码来运行这个测试。 - kumar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接