使用 parallel 对大型文本文件进行排序并打印第二列总和的最佳表达式
Best expression using parallel for sorting a large text file and printing the sum of the second column
目前我有一个两列形式的大文本文件。我正在尝试打印唯一的第一列,并将它们的总和作为输出。
cat src
a 1
b 1
c 1
d 1
a 1
b 2
c 3
d 4
使用基本的 awk,我能够获得所需的输出。
awk -F" " '{a[]+=;}END{for(i in a)print i" "a[i];}' src
a 2
b 3
c 4
d 5
手头的问题是处理 运行s 的时间很长,如果我们 运行 与大型输入文件相同。因此尝试 运行 与 gnu-parallel 相同并在那里进行攻击。
cat src | parallel --pipe awk -F" " '{a[]+=;}END{for(i in a)print i" "a[i];}'
任何关于这方面的指导将不胜感激。
我发现 GNU datamash 在这种情况下 独立 运行 是最快的工具。
测试文件 (https://transfer.sh/hL5xL/file) 有 ~12M 行和大小 116Mb。
这是延长时间的性能统计数据:
$ du -sh inputfile
116M inputfile
$ wc -l inputfile
12520872 inputfile
$ time datamash -W -g1 sum 2 <inputfile > /dev/null
real 0m10.990s
user 0m10.388s
sys 0m0.216s
$ time awk '{ a[] += }END{ for(i in a) print i, a[i] }' inputfile > /dev/null
real 0m12.361s
user 0m11.664s
sys 0m0.196s
$ time parallel -a inputfile --pipepart --block=11M -q awk '{ a[] += }END{ for(i in a) print i, a[i] }' \
| awk '{ a[] += }END{ for(i in a) print i, a[i] }' >/dev/null
real 0m8.660s
user 0m12.424s
sys 0m2.760s
对于并行方法使用组合parallel
+ awk
.
对于最新的 datamash
版本,您可以尝试:
parallel -a inputfile --pipepart --block=11M datamash -sW -g1 sum 2 | datamash -sW -g1 sum 2
如您所见,GNU parallel
被用作最后一种方法,由 2awk
命令的组合组成(一个用于聚合中间结果,另一个用于聚合最终结果)。
这里关键的 GNU parallel
选项是:
--pipepart
Pipe parts of a physical file. --pipepart
works similar to --pipe
, but is much faster.
--block-size
size
Size of block in bytes to read at a time.
在我的测试用例中,我将 --block=11M
指定为主文件大小的 ~10%。在您的情况下,您可以将其调整为 --block=100M
.
我强烈怀疑这不是 awk
的问题。我生成了一个测试文件,与您的类似,有 1 亿行,大小约为 1 GB。第一个字段中大约有 10 万个唯一键。在我速度不太快的笔记本电脑上,您的 awk
命令 运行 只需一分多钟。
在不了解您的计算机的情况下,我猜问题要么是内存不足,要么是速度太慢 I/O。在我的系统上 awk
需要大约 512 兆字节的内存来存储 10 万个键。如果您有数百万个密钥,则需要相应的更多内存,并且可能会遇到内存不足导致交换的问题。交换对于散列数组和随机键非常糟糕。或者,如果您正在从慢速网络文件系统或旧 USB 记忆棒读取文件,您可能只是在等待 I/O,尽管这种可能性较小。
我建议你 运行 你的命令然后用 top
观察它,看看发生了什么。您的 awk
进程应该使用 100% 的 CPU。如果不是,top
应该显示交换问题或 I/O 等待。祝你好运。
您说输入文件已排序,因此您可以大大改进您的 awk
命令:
awk -F" " '{if (key!=) {print key" "sum; key=; sum=0} sum+=}
END {print key" "sum}' inputfile
此命令使用恒定数量的内存,而不是键数的线性数量。由于 ,在您的情况下,内存可能是主要的减速因素。
由于您的示例文件未排序,我们在 sort
之后在管道中测试命令
$ sort src | awk ...
a 2
b 3
c 4
d 5
可以通过在 awk
命令中添加另一个 if
或附加 ... | tail -n +2
.
来删除开头的额外空行
如果您的输入文件未排序,这种方法很慢,即使使用 LC_ALL=C sort
排序更快(与 sort
相比,我的系统花费一半的时间)。
请注意,这只是 awk
命令的改进。 也受益于已经排序的数据并击败 awk
.
目前我有一个两列形式的大文本文件。我正在尝试打印唯一的第一列,并将它们的总和作为输出。
cat src
a 1
b 1
c 1
d 1
a 1
b 2
c 3
d 4
使用基本的 awk,我能够获得所需的输出。
awk -F" " '{a[]+=;}END{for(i in a)print i" "a[i];}' src
a 2
b 3
c 4
d 5
手头的问题是处理 运行s 的时间很长,如果我们 运行 与大型输入文件相同。因此尝试 运行 与 gnu-parallel 相同并在那里进行攻击。
cat src | parallel --pipe awk -F" " '{a[]+=;}END{for(i in a)print i" "a[i];}'
任何关于这方面的指导将不胜感激。
我发现 GNU datamash 在这种情况下 独立 运行 是最快的工具。
测试文件 (https://transfer.sh/hL5xL/file) 有 ~12M 行和大小 116Mb。
这是延长时间的性能统计数据:
$ du -sh inputfile
116M inputfile
$ wc -l inputfile
12520872 inputfile
$ time datamash -W -g1 sum 2 <inputfile > /dev/null
real 0m10.990s
user 0m10.388s
sys 0m0.216s
$ time awk '{ a[] += }END{ for(i in a) print i, a[i] }' inputfile > /dev/null
real 0m12.361s
user 0m11.664s
sys 0m0.196s
$ time parallel -a inputfile --pipepart --block=11M -q awk '{ a[] += }END{ for(i in a) print i, a[i] }' \
| awk '{ a[] += }END{ for(i in a) print i, a[i] }' >/dev/null
real 0m8.660s
user 0m12.424s
sys 0m2.760s
对于并行方法使用组合parallel
+ awk
.
对于最新的 datamash
版本,您可以尝试:
parallel -a inputfile --pipepart --block=11M datamash -sW -g1 sum 2 | datamash -sW -g1 sum 2
如您所见,GNU parallel
被用作最后一种方法,由 2awk
命令的组合组成(一个用于聚合中间结果,另一个用于聚合最终结果)。
这里关键的 GNU parallel
选项是:
--pipepart
Pipe parts of a physical file.--pipepart
works similar to--pipe
, but is much faster.
--block-size
size
Size of block in bytes to read at a time.
在我的测试用例中,我将 --block=11M
指定为主文件大小的 ~10%。在您的情况下,您可以将其调整为 --block=100M
.
我强烈怀疑这不是 awk
的问题。我生成了一个测试文件,与您的类似,有 1 亿行,大小约为 1 GB。第一个字段中大约有 10 万个唯一键。在我速度不太快的笔记本电脑上,您的 awk
命令 运行 只需一分多钟。
在不了解您的计算机的情况下,我猜问题要么是内存不足,要么是速度太慢 I/O。在我的系统上 awk
需要大约 512 兆字节的内存来存储 10 万个键。如果您有数百万个密钥,则需要相应的更多内存,并且可能会遇到内存不足导致交换的问题。交换对于散列数组和随机键非常糟糕。或者,如果您正在从慢速网络文件系统或旧 USB 记忆棒读取文件,您可能只是在等待 I/O,尽管这种可能性较小。
我建议你 运行 你的命令然后用 top
观察它,看看发生了什么。您的 awk
进程应该使用 100% 的 CPU。如果不是,top
应该显示交换问题或 I/O 等待。祝你好运。
您说输入文件已排序,因此您可以大大改进您的 awk
命令:
awk -F" " '{if (key!=) {print key" "sum; key=; sum=0} sum+=}
END {print key" "sum}' inputfile
此命令使用恒定数量的内存,而不是键数的线性数量。由于
由于您的示例文件未排序,我们在 sort
$ sort src | awk ...
a 2
b 3
c 4
d 5
可以通过在 awk
命令中添加另一个 if
或附加 ... | tail -n +2
.
如果您的输入文件未排序,这种方法很慢,即使使用 LC_ALL=C sort
排序更快(与 sort
相比,我的系统花费一半的时间)。
请注意,这只是 awk
命令的改进。 awk
.