
為什麼 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
看起來比其他操作慢得多。