我有一个大约一百万行的文件,如下所示:
"ID" "1" "2"
"00000687" 0 1
"00000421" 1 0
"00000421" 1 0
"00000421" 1 0最后一行重复了100多万次。从这个问题获得灵感,我尝试了一些建议的解决方案,看看哪一个更快。我原以为只有一个进程的解决方案会比有管道的解决方案更快,因为它们只使用一个进程。但这是我的测试结果:
tail -n +2 file.txt | tr -d \" $ time -n +2 file.txt \ tr -d \“1> /dev/空实0m0,032s用户0m0,020 sys 0m0,028ssed '1d;s/"//g' file.txt $ time sed '1d;s/"//g‘file.txt 1> /dev/null实0m0,410 s用户0m0,399 s sys 0m0,011 sperl -ne ' { s/"//g; print if $. > 1 }' file.txt $ time -ne‘{ s/"//g;打印if $. >1 }’file.txt 1> /dev/空实0m0,379 s用户0m0,367 s sys 0m0,013 s我重复了很多次测试,而且我总是得到类似的数字。如您所见,tail -n +2 file.txt | tr -d \"比其他<#>much速度更快。为什么?
发布于 2020-05-21 14:07:52
这可以归结为所做的工作量。
您的tail | tr命令最后执行以下操作:
tail:tr中,阅读,不关心换行符,输出除“”(一个固定字符)以外的所有内容。在解释了给定的脚本后,您的sed命令将执行以下操作:
您的Perl命令在解释了给定的脚本后,将执行以下操作:
寻找新行的结果是在大型输入上花费很大。
发布于 2020-05-21 14:03:27
主要是因为perl和sed分别处理每一行。
如果让perl按更大的块处理输入,并将其简化一点(请参阅注),您可以使它变得更快--但没有比tr更快的了:
time perl -ne ' { s/"//g; print if $. > 1 }' file.txt 1> /dev/null
real 0m0.617s
user 0m0.612s
sys 0m0.005s
time perl -pe 'BEGIN{<>;$/=\40960} s/"//g' file.txt >/dev/null
real 0m0.186s
user 0m0.177s
sys 0m0.009s
time tail -n +2 file.txt | tr -d \" 1> /dev/null
real 0m0.033s
user 0m0.031s
sys 0m0.023s注意:不要使用perl -ne '... if $. > 1'或awk 'NR == 1 { ... } /foo/ { ... }'。
使用BEGIN{<>}和BEGIN{getline}代替。
在您阅读了第一行之后,您可以非常确信以后的行不再是第一行了:不需要一遍又一遍地检查。
发布于 2020-05-21 21:17:21
tail_lines()来自尾.c:
/* Use file_lines only if FD refers to a regular file for
which lseek (... SEEK_END) works. */
if ( ! presume_input_pipe
&& S_ISREG (stats.st_mode)
&& (start_pos = lseek (fd, 0, SEEK_CUR)) != -1
&& start_pos < (end_pos = lseek (fd, 0, SEEK_END)))这个end_pos = lseek (fd, 0, SEEK_END)是跳过文件内容的地方。在file_lines()中有反向扫描,计数换行符。
write()是一个非常简单的系统调用,用于重新定位文件偏移量以进行读/写。
哦,看来我错过了这个问题中的微妙之处),这一切都是关于阅读直线与方块。通常情况下,将多个传球组合成一个复杂的传球是个好主意。但是在这里,算法只需要第一行换行符。
Ole用sysread()编写的两部分perl脚本说明了他如何从搜索第一行换行符(S)切换到读取最大块。
当tail正常工作时,它读取最后一个块并计数换行符。它是从那里打印出来的,或者是读到下一个街区。
https://unix.stackexchange.com/questions/588127
复制相似问题