我通常使用大约20 Gb大小的文本文件,并且发现自己经常对给定文件中的行数进行计数。
我现在要做的就是 cat fname | wc -l
,而且需要很长时间。有什么解决方案会更快吗?
我在安装了Hadoop的高性能集群中工作。我想知道地图缩小方法是否有帮助。
我希望该解决方案像解决方案一样简单,只需一条线即可wc -l
,但不确定其可行性。
有任何想法吗?
我通常使用大约20 Gb大小的文本文件,并且发现自己经常对给定文件中的行数进行计数。
我现在要做的就是 cat fname | wc -l
,而且需要很长时间。有什么解决方案会更快吗?
我在安装了Hadoop的高性能集群中工作。我想知道地图缩小方法是否有帮助。
我希望该解决方案像解决方案一样简单,只需一条线即可wc -l
,但不确定其可行性。
有任何想法吗?
wc -l fname
可能更快。您也可以尝试一下vim -R fname
是否更快(它应该告诉您启动后的行数)。
Answers:
尝试: sed -n '$=' filename
猫也是不必要的:wc -l filename
用您现在的方式就足够了。
sed
,速度更快。也许,进行一些基准测试可以更好地理解它。
wc -l filename
先运行,然后您运行sed -n '$=' filename
,则在第一次运行时wc必须从磁盘读取所有文件,因此可以将其完全缓存在您可能大于3Gb的内存中,因此sed
接下来可以更快地运行。我自己在具有6Gb RAM的计算机上对一个4Gb文件进行了测试,但是我确保文件已经在缓存中了。得分:sed
-0m12.539s,wc -l
-0m1.911s。所以wc
要快6.56倍。重做实验,但在每次运行前清除缓存,它们都花了大约58秒才能完成。
限制速度的因素是存储设备的I / O速度,因此在简单的换行符/模式计数程序之间进行更改将无济于事,因为这些程序之间的执行速度差异可能会因较慢的磁盘/存储/随便你
但是,如果跨磁盘/设备复制了相同的文件,或者该文件分布在这些磁盘之间,则可以并行执行该操作。我不特别了解这个Hadoop,但是假设您可以从4个不同的位置读取10gb的文件,则可以运行4个不同的行计数过程,每个过程都在文件的一部分中,并将它们的结果相加:
$ dd bs=4k count=655360 if=/path/to/copy/on/disk/1/file | wc -l &
$ dd bs=4k skip=655360 count=655360 if=/path/to/copy/on/disk/2/file | wc -l &
$ dd bs=4k skip=1310720 count=655360 if=/path/to/copy/on/disk/3/file | wc -l &
$ dd bs=4k skip=1966080 if=/path/to/copy/on/disk/4/file | wc -l &
注意&
每个命令行上的,因此所有命令将并行运行。dd
的工作方式类似于cat
此处,但允许我们指定要读取的count * bs
字节数(字节)以及在输入的开头要跳过多少字节(skip * bs
字节)。它以块为单位工作,因此需要指定bs
为块大小。在此示例中,我将10Gb文件划分为4个相等的块,分别为4Kb * 655360 = 2684354560字节= 2.5GB,为每个作业分配了一个,您可能希望根据其大小设置脚本来为您做文件以及您将运行的并行作业数。您还需要对执行结果求和,这是我由于缺少Shell脚本功能而没有做的事情。
如果您的文件系统足够智能,可以在多个设备(例如RAID或分布式文件系统等)之间拆分大文件,并自动并行化可以并行化的I / O请求,则可以执行这样的拆分,并运行许多并行作业,但是可以使用相同的文件路径,您仍然可以提高速度。
编辑:我想到的另一个想法是,如果文件中的行大小相同,则可以通过将文件大小除以行大小(以字节为单位)来获得确切的行数。您几乎可以在一项工作中即时完成。如果您具有平均大小并且不完全关心行数,但是想要估算,则可以执行相同的操作,并且比精确操作快得多地获得满意的结果。
在多核服务器上,使用并行GNU并行计算文件行数。在打印每个文件的行数之后,bc将对所有行数求和。
find . -name '*.txt' | parallel 'wc -l {}' 2>/dev/null | paste -sd+ - | bc
为了节省空间,您甚至可以压缩所有文件。下一行解压缩每个文件,并对其并行进行计数,然后对所有计数求和。
find . -name '*.xz' | parallel 'xzcat {} | wc -l' 2>/dev/null | paste -sd+ - | bc
dd
而不是wc
读取文件的答案。
根据我的测试,我可以验证Spark-Shell(基于Scala)比其他工具(GREP,SED,AWK,PERL,WC)要快得多。这是我在具有23782409行的文件上运行的测试结果
time grep -c $ my_file.txt;
真正的0m44.96s用户0m41.59s sys 0m3.09s
time wc -l my_file.txt;
真实的0m37.57s用户0m33.48s sys 0m3.97s
time sed -n '$=' my_file.txt;
实际0m38.22s用户0m28.05s sys 0m10.14s
time perl -ne 'END { $_=$.;if(!/^[0-9]+$/){$_=0;};print "$_" }' my_file.txt
;
实际0m23.38s用户0m20.19s sys 0m3.11s
time awk 'END { print NR }' my_file.txt;
真实0m19.90s用户0m16.76s sys 0m3.12s
spark-shell
import org.joda.time._
val t_start = DateTime.now()
sc.textFile("file://my_file.txt").count()
val t_end = DateTime.now()
new Period(t_start, t_end).toStandardSeconds()
res1:org.joda.time.Seconds = PT15S
time
以获取运行时。
如果您的数据位于HDFS上,则最快的方法可能是使用hadoop流。Apache Pig的COUNT UDF在袋子上运行,因此使用单个化简器来计算行数。相反,您可以在一个简单的hadoop流脚本中手动设置reducer的数量,如下所示:
$HADOOP_HOME/bin/hadoop jar $HADOOP_HOME/hadoop-streaming.jar -Dmapred.reduce.tasks=100 -input <input_path> -output <output_path> -mapper /bin/cat -reducer "wc -l"
请注意,我将减速器的数量手动设置为100,但是您可以调整此参数。一旦完成了map-reduce作业,来自每个reducer的结果将存储在一个单独的文件中。行的最终计数是所有减速器返回的数字之和。您可以按以下方式获得行的最终计数:
$HADOOP_HOME/bin/hadoop fs -cat <output_path>/* | paste -sd+ | bc
我知道这个问题已经有好几年了,但是根据Ivella的最后一个想法,这个bash脚本通过测量一行的大小并从中推断出来,从而在几秒钟或更短的时间内估算出一个大文件的行数:
#!/bin/bash
head -2 $1 | tail -1 > $1_oneline
filesize=$(du -b $1 | cut -f -1)
linesize=$(du -b $1_oneline | cut -f -1)
rm $1_oneline
echo $(expr $filesize / $linesize)
如果您将此脚本命名为lines.sh
,则可以调用lines.sh bigfile.txt
以获取估计的行数。在我的情况下(从数据库导出大约6 GB),与真实行数的偏差仅为3%,但运行速度快了大约1000倍。顺便说一句,我使用第二行而不是第一行作为基础,因为第一行具有列名,而实际数据从第二行开始。
Hadoop本质上提供了一种执行类似于@Ivella所建议的功能的机制。
Hadoop的HDFS(分布式文件系统)将获取您20GB的文件,并将其以固定大小的块保存在整个群集中。假设您将块大小配置为128MB,则文件将被拆分为20x8x128MB块。
然后,您将对此数据运行map reduce程序,实质上是在map阶段对每个块的行进行计数,然后将这些块的行数减少为整个文件的最终行数。
至于性能,通常来说,集群越大,性能越好(wc在更多独立磁盘上并行运行更多的wc),但是作业编排中存在一些开销,这意味着在较小的文件上运行该作业实际上不会更快地产生结果。吞吐量比运行本地wc
我不确定python更快吗:
[root@myserver scripts]# time python -c "print len(open('mybigfile.txt').read().split('\n'))"
644306
real 0m0.310s
user 0m0.176s
sys 0m0.132s
[root@myserver scripts]# time cat mybigfile.txt | wc -l
644305
real 0m0.048s
user 0m0.017s
sys 0m0.074s
...read().split("\n")
。为此sum(1 for line in open("mybigfile.txt"))
,您将拥有更好的幼稚方法(即不从HDFS设置中获得任何好处)
如果您的计算机装有python,则可以从外壳程序中尝试使用此命令:
python -c "print len(open('test.txt').read().split('\n'))"
这使用 python -c
传递一个命令,该命令基本上是读取文件,然后按“换行符”进行拆分,以获取换行数或文件的总长度。
bash-3.2$ sed -n '$=' test.txt
519
使用上面的:
bash-3.2$ python -c "print len(open('test.txt').read().split('\n'))"
519
python -c "print(sum(1 for line in open('text.txt'))"
在python中将是更好的解决方案,因为它不会将整个文件读入内存,但是sed或wc会是更好的解决方案。
find -type f -name "filepattern_2015_07_*.txt" -exec ls -1 {} \; | cat | awk '//{ print $0 , system("cat " $0 "|" "wc -l")}'
输出:
让我们假设:
那么您真的想将文件分成多个部分,在多个节点上并行计数部分,然后从那里总结结果(这基本上是@Chris White的想法)。
这是您使用GNU Parallel(版本> 20161222)执行的操作。您需要列出其中的节点,~/.parallel/my_cluster_hosts
并且必须有权ssh
访问所有节点:
parwc() {
# Usage:
# parwc -l file
# Give one chunck per host
chunks=$(cat ~/.parallel/my_cluster_hosts|wc -l)
# Build commands that take a chunk each and do 'wc' on that
# ("map")
parallel -j $chunks --block -1 --pipepart -a "$2" -vv --dryrun wc "$1" |
# For each command
# log into a cluster host
# cd to current working dir
# execute the command
parallel -j0 --slf my_cluster_hosts --wd . |
# Sum up the number of lines
# ("reduce")
perl -ne '$sum += $_; END { print $sum,"\n" }'
}
用于:
parwc -l myfile
parwc -w myfile
parwc -c myfile
我有一个645GB的文本文件,并且之前的确切解决方案(例如wc -l
)都没有在5分钟内返回答案。
相反,这是Python脚本,用于计算一个大文件中的大约行数。(我的文本文件显然有大约55亿行。)Python脚本执行以下操作:
A.计算文件中的字节数。
B.读取N
文件中的第一行(作为示例)并计算平均行长。
C.将A / B计算为大约的行数。
它遵循Nico的答案,但不是计算一行的长度,而是计算前几行的平均长度N
。
注意:我假设使用ASCII文本文件,因此我希望Pythonlen()
函数返回chars作为字节数。
将此代码放入文件中line_length.py
:
#!/usr/bin/env python
# Usage:
# python line_length.py <filename> <N>
import os
import sys
import numpy as np
if __name__ == '__main__':
file_name = sys.argv[1]
N = int(sys.argv[2]) # Number of first lines to use as sample.
file_length_in_bytes = os.path.getsize(file_name)
lengths = [] # Accumulate line lengths.
num_lines = 0
with open(file_name) as f:
for line in f:
num_lines += 1
if num_lines > N:
break
lengths.append(len(line))
arr = np.array(lengths)
lines_count = len(arr)
line_length_mean = np.mean(arr)
line_length_std = np.std(arr)
line_count_mean = file_length_in_bytes / line_length_mean
print('File has %d bytes.' % (file_length_in_bytes))
print('%.2f mean bytes per line (%.2f std)' % (line_length_mean, line_length_std))
print('Approximately %d lines' % (line_count_mean))
用N
= 5000这样调用它。
% python line_length.py big_file.txt 5000
File has 645620992933 bytes.
116.34 mean bytes per line (42.11 std)
Approximately 5549547119 lines
因此,文件中大约有55亿行。