为什么上厕所这么慢?

为什么上厕所这么慢?

为什么 wc 实用程序这么慢?

当我在大文件上运行它时,它花费的时间比 md5sum 长大约 20 倍:

MyDesktop:/tmp$ dd if=/dev/zero bs=1024k count=1024 of=/tmp/bigfile
1024+0 records in
1024+0 records out
1073741824 bytes (1.1 GB) copied, 0.687094 s, 1.6 GB/s

MyDesktop:/tmp$ time wc /tmp/bigfile 
         0          0 1073741824 /tmp/bigfile

real    0m45.969s
user    0m45.424s
sys     0m0.424s

MyDesktop:/tmp$ time md5sum /tmp/bigfile 
cd573cfaace07e7949bc0c46028904ff  /tmp/bigfile

real    0m2.520s
user    0m2.196s
sys     0m0.316s

这不仅仅是由于文件充满空值而导致的奇怪边缘条件,即使文件充满随机数据或者是文本文件,我也看到了相同的性能差异。

(这是在 Ubuntu 13.04,64 位上)

答案1

所以我去了源代码,看起来缓慢是在处理双字节字符时。本质上,对于读入的每个字符,都需要调用mbrtowc()尝试将其转换为宽字符,然后测试该宽字符以查看它是否是单词分隔符、行分隔符等。

事实上,如果我更改LANG默认的语言环境变量en_US.UTF-8(UTF-8 是多字节字符集)并将其设置为“ C”(简单的单字节字符集),wc则能够使用单字节优化,从而大大加快速度,只需要以前的四分之一左右的时间。

此外,它只需检查每个字符是否进行单词 ( -w)、行长度 ( -L) 或字符 ( -m) 计数。如果它只进行字节和/或行计数,它可以跳过宽字符处理,然后运行得非常快——比md5sum.

我运行了它gprof,用于处理多字节字符(mymbsinit()mymbrtowc()myiswprint()等)的函数仅占用了大约 30% 的执行时间,并且单步执行缓冲区的代码要复杂得多,因为它必须处理可变大小字符在缓冲区中的可变大小步骤,以及将跨越缓冲区的任何部分完成的字符填充回缓冲区的开头,以便下次可以处理它。

现在我知道要寻找什么了,我发现了一些帖子提到一些实用程序的 utf-8 速度慢:

https://stackoverflow.com/questions/13913014/grepping-a-huge-file-80gb-any-way-to-speed-it-up http://dtrace.org/blogs/brendan/2011/12/08/2000x-performance-win/

答案2

只是一个猜测,但你有点将苹果与橙子进行比较,比较wc正在做什么和md5sum正在做什么。

md5sum的任务

md5sum处理文件时,它只是将文件作为流打开,然后开始通过MD5校验功能需要很少的内存。它本质上是 CPU 和磁盘 I/O 限制。

厕所的任务

wc运行时,它会做更多的事情,而不仅仅是一次解析文件一个字符。它必须实际分析文件的结构,一次一行地确定字符之间的边界在哪里以及是否是单词边界。

例子

考虑以下字符串以及每种算法在解析它们时必须如何遍历它们:

“Hello! Greg”
“Hello!Greg”
“Hello\nGreg”
“A.D.D.”
“Wow, how great!”
“wow     \n\n\n    great”
“it was a man-eating shark.”

对于 MD5,它每次在这些字符串中移动一个字符。因为wc它必须决定什么是单词和行边界并跟踪它看到的出现次数。

其他厕所讨论

我找到了这个2006 年的编码挑战讨论wc在 .NET 中的实现。当您查看一些伪代码时,困难是非常明显的,因此这可能有助于开始阐明为什么wc看起来比其他操作慢得多。

相关内容