在大文件中计数行
我通常使用大小为20 Gb的文本文件,而且我发现自己经常计算给定文件中的行数。
我现在这样做只是cat fname | wc -l
cat fname | wc -l
,需要很长时间。 有什么解决scheme会更快?
我在安装了Hadoop的高性能集群中工作。 我想知道如果减less地图的方法可以帮助。
我希望解决scheme像一条线一样简单,就像wc -l
解决scheme一样,但不知道它是多么的可行。
有任何想法吗?
试试: sed -n '$=' filename
另外,猫也是不必要的: wc -l filename
就足够了。
您的限制速度因素是您的存储设备的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
( count * bs
bytes)以及在input开始时跳过多less字节( skip * bs
bytes)。 它在块中工作,因此,需要指定bs
作为块大小。 在这个例子中,我已经将这个10Gb文件分成了4Kb * 655360 = 2684354560个字节= 2.5GB的4个等分块,给每个作业分配一个,你可能需要设置一个脚本来为你做基于文件和您将运行的并行作业数量。 你还需要总结执行的结果,我还没有为我的shell脚本能力缺乏做过什么。
如果你的文件系统足够聪明,可以将大文件分割成许多设备,比如RAID或分布式文件系统等等,并且自动并行处理I / O请求,你可以做这样的分割,运行许多并行作业,但是使用相同的文件path,你仍然可能有一些速度增益。
编辑:我想到的另一个想法是,如果文件内的行具有相同的大小,可以通过将文件的大小除以行的大小,以字节为单位来获得确切的行数。 你几乎可以在一份工作中立即做到这一点。 如果您的平均尺寸并不精确,但是需要进行估算,则可以进行相同的操作,并获得令人满意的结果,比准确的操作快得多。
如果你的数据驻留在HDFS上,或许最快的方法是使用hadoopstream。 Apache Pig的COUNT UDF使用一个包来操作,因此使用一个reducer来计算行数。 相反,您可以手动设置一个简单的hadoopstream脚本中的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
在多核服务器上,使用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
Hadoop本质上提供了一种机制来执行类似于@Ivella所build议的function。
Hadoop的HDFS(分布式文件系统)将把你的20GB文件以一个固定大小的块保存在集群中。 假设你将块大小configuration为128MB,文件将被分割成20x8x128MB块。
然后,你可以在这个数据上运行一个map reduce程序,基本上为每个block(在map阶段)计算行数,然后把这些block数量减less到整个文件的最后行数。
至于性能,一般说来,集群越大,性能就越好(更多的wc并行运行,而不是更多的独立磁盘),但是在作业编排上有一些开销,这意味着在较小的文件上运行作业实际上并不会更快吞吐量比运行本地wc
根据我的testing,我可以validationSpark-Shell(基于Scala)比其他工具(GREP,SED,AWK,PERL,WC)快得多。 这是我在23782409行文件上运行的testing结果
time grep -c $ my_file.txt;
实际0m44.96s用户0m41.59s sys 0m3.09s
time wc -l my_file.txt;
实际0m37.57s用户0m33.48s系统0m3.97s
time sed -n '$=' my_file.txt;
实际0m38.22s用户0m28.05s系统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系统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
我不确定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
如果你的电脑有python,你可以从shell里试试这个:
python -c "print len(open('test.txt').read().split('\n'))"
这使用python -c
传递一个命令,它基本上读取文件,并通过“换行符”拆分,以获得换行的数量或文件的总长度。
@ BlueMoon的 :
bash-3.2$ sed -n '$=' test.txt 519
使用以上:
bash-3.2$ python -c "print len(open('test.txt').read().split('\n'))" 519
find -type f -name“filepattern_2015_07 _ *。txt”-exec ls -1 {} \; | 猫| awk'// {print $ 0,system(“cat”$ 0“|”“wc -l”)}'
输出:
我知道这个问题现在已经有几年了,但是扩展了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倍。 顺便说一句,我用第二行而不是第一行作为基础,因为第一行有列名,实际数据从第二行开始。
如果你的瓶颈是磁盘,那么你是如何读取它的。 dd if=filename bs=128M | wc -l
dd if=filename bs=128M | wc -l
比wc -l filename
或者cat filename | wc -l
要快得多 cat filename | wc -l
用于具有硬盘和快速CPU和RAM的机器。 你可以玩块的大小,看看dd
报告吞吐量。 我把它升到1GiB。
注意:有关cat
或dd
是否更快的争论。 我所说的是, dd
可以更快,这取决于系统,这是我的。 自己尝试一下。