为什么我的 awk 脚本比 head+tail 脚本慢得多?
Why is my awk script much slower than the head+tail script?
我想拆分一个巨大的文件 (big.txt)。通过给定的行号。例如,如果给出的数字是 10 15 30
,我将得到 4 个文件:1-10
、11-15
、16-30
和 big.txt 的 30 - EOF
].
解决问题对我来说不是挑战,我写了 3 种不同的解决方案。但是,我无法解释性能。为什么 awk 脚本是最慢的。 (GNU Awk)
对于 big.txt,我刚刚做了 seq 1.5billion > big.txt (~15Gb)
首先是头尾:
INPUT_FILE="big.txt" # The input file
LINE_NUMBERS=( 400000 700000 1200000 ) # Given line numbers
START=0 # The offset to calculate lines
IDX=1 # The index used in the name of generated files: file1, file2 ...
for i in "${LINE_NUMBERS[@]}"
do
# Extract the lines
head -n $i "$INPUT_FILE" | tail -n +$START > "file$IDX.txt"
#
(( IDX++ ))
START=$(( i+1 ))
done
# Extract the last given line - last line in the file
tail -n +$START "$INPUT_FILE" > "file$IDX.txt"
第二个:sed:
INPUT_FILE="big.txt" # The input file
LINE_NUMBERS=( 400000 700000 1200000 ) # Given line numbers
START=1 # The offset to calculate lines
IDX=1 # The index used in the name of generated files: file1, file2 ...
for i in "${LINE_NUMBERS[@]}"
do
T=$(( i+1 ))
# Extract the lines using sed command
sed -n -e " $START, $i p" -e "$T q" "$INPUT_FILE" > "file$IDX.txt"
(( IDX++ ))
START=$T
done
# Extract the last given line - last line in the file
sed -n "$START, $ p" "$INPUT_FILE" > "file$IDX.txt"
最后一个,awk
awk -v nums="400000 700000 1200000" 'BEGIN{c=split(nums,a)} {
for(i=1; i<=c; i++){
if( NR<=a[i] ){
print > "file" i ".txt"
next
}
}
print > "file" c+1 ".txt"
}' big.txt
根据我的测试(使用 time
命令),head+tail 是最快的:
real 73.48
user 1.42
sys 17.62
sed一个:
real 144.75
user 105.68
sys 15.58
awk一个:
real 234.21
user 187.92
sys 3.98
awk 只遍历了一次文件,为什么它比其他两个慢很多?另外,我认为尾部和头部将是最慢的解决方案,怎么这么快?我想这可能与 awk 的重定向有关? (打印 > 文件)
谁能给我解释一下?谢谢。
对于 awk,每一行都需要一个循环、比较和创建文件名。也许 awk 还执行解析每一行的艰巨任务。
您可能想尝试以下实验
- 试试 mawk(awk 的快速实现)并检查它是否快得多。
- 删除
print > "file" i ".txt"
看看它节省了多少时间。
awk
可以比 head
和 tail
快吗?
不,它会更慢,至少对于大型输入文件的合理数量的块而言。因为它会读取每一行并对其进行一些处理。另一方面,head
和 tail
将大量读取换行符,什么都不做,将查找直到找到参数提供的行号。然后他们就不用再逐行阅读并决定做什么,而是转储内容,类似于 cat
.
如果我们增加块的数量,如果分割线数的数组越来越大,那么我们将达到调用许多head
和tail
进程的成本将克服一个 awk
过程的成本,从那时起,awk
会更快。
awk
脚本改进
这个 awk
很慢,因为那个循环!试想对于最后一个输出文件,对于要打印的每一行,我们 运行 4 次迭代直到我们打印该行。当然,时间复杂度仍然与输入保持线性关系,但是所有这些检查和分配都会随着输入的增长而产生成本。它可以得到很大的改进,例如像这样:
> cat tst.awk
BEGIN {
a[1]
a[40000]
a[70000]
a[120000]
}
NR in a {
close(out)
out = "file" ++i ".txt"
}
{ print > out }
这里我们每行只测试NR,实际上我们几乎只打印。
awk -f tst.awk big.txt
测试
这是一些基本测试,我做了一个文件,不大,5.2M 行。
> wc -l big.txt
5288558 big.txt
现在,有了这个循环,在哪里拆分文件真的很重要!如果你必须将大部分行写入最后一个块,这意味着更多的迭代,它更慢
> head -1 test.sh
awk -v nums="100000 200000 300000" 'BEGIN{c=split(nums,a)} {
> time sh test.sh
real 0m10.960s
user 0m10.823s
sys 0m0.066s
如果大多数行转到第一个文件(这意味着一个迭代和下一个)它变得更快!
> head -1 test.sh
awk -v nums="5000000 5100000 5200000" 'BEGIN{c=split(nums,a)} {
> time sh test.sh
real 0m6.914s
user 0m6.838s
sys 0m0.043s
经过上述修改,无论切点如何,都应该足够快。
> time awk -f tst.awk big.txt
real 0m4.270s
user 0m4.185s
sys 0m0.048s
我想拆分一个巨大的文件 (big.txt)。通过给定的行号。例如,如果给出的数字是 10 15 30
,我将得到 4 个文件:1-10
、11-15
、16-30
和 big.txt 的 30 - EOF
].
解决问题对我来说不是挑战,我写了 3 种不同的解决方案。但是,我无法解释性能。为什么 awk 脚本是最慢的。 (GNU Awk)
对于 big.txt,我刚刚做了 seq 1.5billion > big.txt (~15Gb)
首先是头尾:
INPUT_FILE="big.txt" # The input file
LINE_NUMBERS=( 400000 700000 1200000 ) # Given line numbers
START=0 # The offset to calculate lines
IDX=1 # The index used in the name of generated files: file1, file2 ...
for i in "${LINE_NUMBERS[@]}"
do
# Extract the lines
head -n $i "$INPUT_FILE" | tail -n +$START > "file$IDX.txt"
#
(( IDX++ ))
START=$(( i+1 ))
done
# Extract the last given line - last line in the file
tail -n +$START "$INPUT_FILE" > "file$IDX.txt"
第二个:sed:
INPUT_FILE="big.txt" # The input file
LINE_NUMBERS=( 400000 700000 1200000 ) # Given line numbers
START=1 # The offset to calculate lines
IDX=1 # The index used in the name of generated files: file1, file2 ...
for i in "${LINE_NUMBERS[@]}"
do
T=$(( i+1 ))
# Extract the lines using sed command
sed -n -e " $START, $i p" -e "$T q" "$INPUT_FILE" > "file$IDX.txt"
(( IDX++ ))
START=$T
done
# Extract the last given line - last line in the file
sed -n "$START, $ p" "$INPUT_FILE" > "file$IDX.txt"
最后一个,awk
awk -v nums="400000 700000 1200000" 'BEGIN{c=split(nums,a)} {
for(i=1; i<=c; i++){
if( NR<=a[i] ){
print > "file" i ".txt"
next
}
}
print > "file" c+1 ".txt"
}' big.txt
根据我的测试(使用 time
命令),head+tail 是最快的:
real 73.48
user 1.42
sys 17.62
sed一个:
real 144.75
user 105.68
sys 15.58
awk一个:
real 234.21
user 187.92
sys 3.98
awk 只遍历了一次文件,为什么它比其他两个慢很多?另外,我认为尾部和头部将是最慢的解决方案,怎么这么快?我想这可能与 awk 的重定向有关? (打印 > 文件)
谁能给我解释一下?谢谢。
对于 awk,每一行都需要一个循环、比较和创建文件名。也许 awk 还执行解析每一行的艰巨任务。
您可能想尝试以下实验
- 试试 mawk(awk 的快速实现)并检查它是否快得多。
- 删除
print > "file" i ".txt"
看看它节省了多少时间。
awk
可以比 head
和 tail
快吗?
不,它会更慢,至少对于大型输入文件的合理数量的块而言。因为它会读取每一行并对其进行一些处理。另一方面,head
和 tail
将大量读取换行符,什么都不做,将查找直到找到参数提供的行号。然后他们就不用再逐行阅读并决定做什么,而是转储内容,类似于 cat
.
如果我们增加块的数量,如果分割线数的数组越来越大,那么我们将达到调用许多head
和tail
进程的成本将克服一个 awk
过程的成本,从那时起,awk
会更快。
awk
脚本改进
这个 awk
很慢,因为那个循环!试想对于最后一个输出文件,对于要打印的每一行,我们 运行 4 次迭代直到我们打印该行。当然,时间复杂度仍然与输入保持线性关系,但是所有这些检查和分配都会随着输入的增长而产生成本。它可以得到很大的改进,例如像这样:
> cat tst.awk
BEGIN {
a[1]
a[40000]
a[70000]
a[120000]
}
NR in a {
close(out)
out = "file" ++i ".txt"
}
{ print > out }
这里我们每行只测试NR,实际上我们几乎只打印。
awk -f tst.awk big.txt
测试
这是一些基本测试,我做了一个文件,不大,5.2M 行。
> wc -l big.txt
5288558 big.txt
现在,有了这个循环,在哪里拆分文件真的很重要!如果你必须将大部分行写入最后一个块,这意味着更多的迭代,它更慢
> head -1 test.sh
awk -v nums="100000 200000 300000" 'BEGIN{c=split(nums,a)} {
> time sh test.sh
real 0m10.960s
user 0m10.823s
sys 0m0.066s
如果大多数行转到第一个文件(这意味着一个迭代和下一个)它变得更快!
> head -1 test.sh
awk -v nums="5000000 5100000 5200000" 'BEGIN{c=split(nums,a)} {
> time sh test.sh
real 0m6.914s
user 0m6.838s
sys 0m0.043s
经过上述修改,无论切点如何,都应该足够快。
> time awk -f tst.awk big.txt
real 0m4.270s
user 0m4.185s
sys 0m0.048s