77

私は通常、最大20 Gbサイズのテキストファイルを使用しますが、特定のファイルの行数を頻繁に数えています。

今のやり方はただのことでcat fname | wc -l、とても時間がかかります。はるかに高速な解決策はありますか?

Hadoopがインストールされた高性能クラスターで作業しています。マップリデュースアプローチが役立つかどうか疑問に思いました。

ソリューションは、ソリューションのように1行の実行と同じくらい単純にしたいと思いますが、wc -lそれがどれほど実行可能かはわかりません。

何か案は?

4

14 に答える 14

112

試す:sed -n '$=' filename

また、猫は不要ですwc -l filename。現在の方法で十分です。

于 2012-10-03T20:45:39.127 に答える
16

制限速度係数はストレージデバイスのI/O速度であるため、単純な改行/パターンカウントプログラムを変更しても効果はありません。これらのプログラム間の実行速度の違いは、低速のディスク/ストレージ/によって抑制される可能性が高いためです。あなたが持っているものは何でも。

ただし、同じファイルをディスク/デバイス間でコピーしている場合、またはファイルがそれらのディスク間で分散している場合は、確実に並行して操作を実行できます。このHadoopについて具体的にはわかりませんが、4つの異なる場所から10 GBのファイルを読み取ることができると仮定すると、それぞれがファイルの一部で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 * bsbsブロックで動作するため、ブロックサイズとして指定する必要があります。この例では、10Gbファイルを4Kbの4つの等しいチャンクに分割しました* 655360 =2684354560バイト=2.5GB、各ジョブに1つずつ、サイズに基づいてスクリプトを設定することをお勧めしますファイルと実行する並列ジョブの数。また、実行の結果を合計する必要があります。これは、シェルスクリプトの能力が不足しているために私が行っていないことです。

ファイルシステムがRAIDや分散ファイルシステムなどの多くのデバイス間で大きなファイルを分割し、並列化できるI / O要求を自動的に並列化するのに十分スマートな場合は、そのような分割を実行して、多くの並列ジョブを実行できますが、同じファイルパスでも、速度がいくらか向上する可能性があります。

編集:私が思いついたもう1つのアイデアは、ファイル内の行が同じサイズの場合、ファイルのサイズを行のサイズ(両方ともバイト単位)で割ることで正確な行数を取得できるということです。1つのジョブでほぼ瞬時に実行できます。平均サイズがあり、行数を正確に気にしないが、見積もりが必要な場合は、これと同じ操作を実行して、正確な操作よりもはるかに高速に満足のいく結果を得ることができます。

于 2012-10-03T22:25:03.557 に答える
9

私のテストによると、Spark-Shell(Scalaに基づく)が他のツール(GREP、SED、AWK、PERL、WC)よりもはるかに高速であることを確認できます。これは、23782409行のファイルで実行したテストの結果です。

time grep -c $ my_file.txt;

実際の0m44.96sユーザー0m41.59ssys0m3.09s

time wc -l my_file.txt;

実際の0m37.57sユーザー0m33.48ssys0m3.97s

time sed -n '$=' my_file.txt;

実際の0m38.22sユーザー0m28.05ssys0m10.14s

time perl -ne 'END { $_=$.;if(!/^[0-9]+$/){$_=0;};print "$_" }' my_file.txt;

実際の0m23.38sユーザー0m20.19ssys0m3.11s

time awk 'END { print NR }' my_file.txt;

実際の0m19.90sユーザー0m16.76ssys0m3.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

于 2016-10-03T13:47:59.103 に答える
8

マルチコアサーバーでは、GNU parallelを使用して、ファイル行を並列にカウントします。各ファイルの行数が出力された後、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
于 2016-02-25T17:56:49.747 に答える
6

データがHDFSにある場合、おそらく最速のアプローチはHadoopストリーミングを使用することです。ApachePigのCOUNTUDFはバッグで動作するため、単一のレデューサーを使用して行数を計算します。代わりに、次のように単純なHadoopストリーミングスクリプトでレデューサーの数を手動で設定できます。

$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ジョブが完了すると、各レデューサーからの結果が個別のファイルに保存されます。行の最終的な数は、すべてのレデューサーによって返される数の合計です。次のように、最終的な行数を取得できます。

$HADOOP_HOME/bin/hadoop fs -cat <output_path>/* | paste -sd+ | bc
于 2014-08-27T21:56:00.653 に答える
5

質問は数年前のものですが、Ivellaの最後のアイデアを拡張して、このbashスクリプトは、1行のサイズを測定し、そこから推定することにより、数秒以内に大きなファイルの行数を推定します

#!/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倍でした。ちなみに、最初の行には列名があり、実際のデータは2番目の行から始まっているため、最初の行ではなく2番目の行をベースとして使用しました。

于 2017-05-06T14:01:14.873 に答える
3

Hadoopは基本的に、@Ivellaが提案しているものと同様の何かを実行するメカニズムを提供しています。

HadoopのHDFS(分散ファイルシステム)は、20 GBのファイルを取得し、クラスター全体で固定サイズのブロックに保存します。ブロックサイズを128MBに設定すると、ファイルは20x8x128MBのブロックに分割されます。

次に、このデータに対してマップリデュースプログラムを実行し、基本的に各ブロックの行数を(マップステージで)カウントしてから、これらのブロックライン数をファイル全体の最終行数に減らします。

パフォーマンスに関しては、一般に、クラスターが大きいほどパフォーマンスは向上します(より多くのwcが並行して実行され、より多くの独立したディスク上で実行されます)が、ジョブオーケストレーションにはオーバーヘッドがあり、小さなファイルでジョブを実行しても実際には速くなりません。ローカルWCを実行するよりもスループット

于 2012-10-04T01:20:39.280 に答える
2

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
于 2015-05-05T08:06:31.477 に答える
2

ボトルネックがディスクである場合、それからどのように読み取るかが重要です。HDDと高速のCPUとRAMを搭載した私のマシンよりもはるかdd if=filename bs=128M | wc -l高速wc -l filenameです。cat filename | wc -lブロックサイズを試して、どのddレポートがスループットとして報告されるかを確認できます。1GiBまでクランクアップしました。

cat注:高速かどうかについては、いくつかの議論がありddます。私が主張するのはdd、システムによってはより速くなる可能性があること、そしてそれは私のためであるということだけです。自分で試してみてください。

于 2017-05-06T01:41:33.740 に答える
1

コンピューターにPythonが搭載されている場合は、シェルからこれを試すことができます。

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
于 2014-07-11T02:16:51.407 に答える
1
find  -type f -name  "filepattern_2015_07_*.txt" -exec ls -1 {} \; | cat | awk '//{ print $0 , system("cat " $0 "|" "wc -l")}'

出力:

于 2015-07-30T13:19:31.020 に答える
1

645GBのテキストファイルがありますが、以前の正確なソリューション(例wc -l)はどれも5分以内に回答を返しませんでした。

代わりに、巨大なファイルのおおよその行数を計算するPythonスクリプトを次に示します。(私のテキストファイルには明らかに約55億行あります。)Pythonスクリプトは次のことを行います。

A.ファイルのバイト数をカウントします。

B.Nファイルの最初の行を(サンプルとして)読み取り、平均行長を計算します。

C. A/Bをおおよその行数として計算します。

それはニコの答えの線に沿っていますが、1行の長さをとる代わりに、最初のN行の平均の長さを計算します。

注:ASCIIテキストファイルを想定しているので、Pythonlen()関数が文字数をバイト数として返すことを期待しています。

このコードをファイルに入れます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億行が含まれています。

于 2020-05-07T04:57:02.533 に答える
0

仮定しましょう:

  • ファイルシステムが配布されます
  • ファイルシステムは、単一ノードへのネットワーク接続を簡単に満たすことができます
  • 通常のファイルと同じようにファイルにアクセスします

次に、ファイルをパーツに分割し、複数のノードでパーツを並行してカウントし、そこからの結果を合計します(これは基本的に@Chris Whiteのアイデアです)。

これがGNUParallel(バージョン> 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
于 2018-05-19T08:19:47.910 に答える
0

低速のIOがフォールバックするとdd if={file} bs=128M | wc -l、wcがチャーンスルーするためのデータを収集する際に非常に役立ちます。

私もつまずいた

https://github.com/crioux/turbo-linecount

これは素晴らしいです。

于 2021-03-06T00:06:57.340 に答える