1

かなり大きなデータ セット (10K ファイル、それぞれ 20K 行) があります。ファイルと行を交換する必要があります (それぞれ 10K 行の 20K ファイルを用意します)。

すべてを1つの巨大なテーブルに結合し、カットで列を抽出するソリューションがありました..しかし、カットに時間がかかりすぎました(ファイルがキャッシュにある場合でも、4GBのファイルを10K回スキャンするのは正確には高速ではありません)。

だから私はawkで(驚くほど単純な)ワンススルーを書きました:

awk '{ print >> "times/"FNR".txt" }' posns/*

これで問題はありませんが、かなり遅くなります (入力ファイルごとに約 10 秒)。私の推測では、それはまったく必要ないという事実にもかかわらず、フィールドの分離を行っているということです。その機能を無効にして高速化する方法はありますか? それとも、さらに別の言語で解決策を書かなければなりませんか?

それが役立つ場合、私は一般的な解決策を好みますが、各ファイルの各行%d %lf %lfは . )。

4

4 に答える 4

1

これがawkよりも速いかどうかはわかりませんが、タスクを実行するperlスクリプトは次のとおりです。

#!/usr/bin/perl

use strict;
use warnings;

my $line=0;

foreach(@ARGV){

 open (MYINFILE, $_);
 $line=0;

 while(<MYINFILE>){
  $line++;
  open (MYOUTFILE,">>times/$line.txt");
  print MYOUTFILE $_;
  close (MYOUTFILE);
 }

}
于 2012-12-07T21:15:31.660 に答える
1

別の awk を試すことができます。mawk は他の awk よりも高速であり、GNu awk にはいくつかのパフォーマンスの向上があると聞いています。つまり、使用しているものよりも速く実行される可能性があります。フィールドセパレーターをレコードセパレーターに設定すると、1行に1つのフィールドしかないため、フィールド分割が問題であることが正しい場合は、速度が向上する可能性があります。また、間違ったリダイレクト演算子を使用しています - ">>" ではなく ">" を使用する必要があり、文字列の連結が遅いため、番号付きのファイルに印刷してから、後ですべての名前を変更することをお勧めします。

このようなもの:

cd times
awk -F'\n' '{ print > FNR }' ../posns/*
for f in *
do
    mv -- "$f" "${f}.txt"
done
cd ..

最初にダミー ディレクトリでテストすることをお勧めします。

このスレッドの他のコメントを書いて、非常に多くのファイルを同時に開いたままにしておくことが問題である可能性があります。ファイル名のパターンに基づいてサブグループにそれを行うことができますか? たとえば、posns ファイルがすべて数字で始まっている場合:

cd times
rm -f *
for ((i=0; i<=9; i++))
do
   awk -F'\n' '{ print >> FNR }' ../posns/"$i"*
   for f in *
   do
      mv -- "$f" "${f}.txt"
   done
done
cd ..

その場合、最初に出力ファイルを圧縮する必要があることに注意してください。それよりもファイルをグループ化するためのより良い方法があると確信していますが、命名規則があるかどうかをお知らせください.

于 2012-12-07T19:59:02.997 に答える
0

これはスプリットにぴったりの仕事のようですね;)

find posns -type f -exec split -l 10000 {} \;

結果ファイルのサフィックスをカスタマイズするためのオプションを試すこと-aができます。-d

説明:

  • find posns -type f: ディレクトリ内のすべてのファイルを (再帰的に) 検索しますposns
  • -exec ... \;: 見つかった結果ごとに、次のコマンドを実行します...
  • split -l 10000 {}:{}と組み合わせて使用​​すると、 find の結果が に代入され-execます。split -l 10000入力ファイルをそれぞれ最大 10k 行のチャンクに分割します。
于 2012-12-07T19:53:22.400 に答える
0

結局、私はプリティ シェル メソッドをあきらめて、C で別のバージョンを書きました。悲しいことに、プリティ シェルではありませんが、3 桁以上高速です (合計実行時間は 43 秒です。事前にキャッシュされたデータが与えられた awk メソッド)。十分な数のファイルを開くことができるように ulimit を変更する必要があり、行が LINE_LENGTH よりも長い場合、正しく機能しません。

それでも、次善のソリューションよりも 2300 倍速く実行されます。

誰かがこのタスクを実行しようとしてこれに出くわした場合、これで実行できます。注意して、実際に機能することを確認してください。

    #include <stdio.h>
    #include <stdlib.h>

    #define LINE_LENGTH 1024

    int main(int argc, char* argv[]) {
            int fn;
            int ln;
            char read[LINE_LENGTH];

            int fmax=10;
            int ftot=0;
            FILE** files=malloc(fmax*sizeof(FILE*));
            char fname[255];
            printf("%d arguments\n", argc);

            printf("opening %s\n",argv[1]);
            FILE* open = fopen(argv[1],"r");

            for(ln=0;fgets(read,LINE_LENGTH,open); ln++) {
                    if(ln==fmax) {
                            printf("%d has reached %d; reallocing\n",ln,fmax);
                            fmax*=2;
                            files=realloc(files,fmax*sizeof(FILE*));
                    }
                    sprintf(fname, "times/%09d.txt",ln);
                    files[ln]=fopen(fname,"w");
                    if(files[ln]==0) {
                            fprintf(stderr,"Failed at opening file number %d\n",ln);
                            return 1;
                    }
                    fprintf(files[ln],"%s",read);
            }
            ftot=ln;
            fclose(open);

            for(fn=2;fn<argc;fn++) {
                    printf("working on file %d\n",fn);
                    open=fopen(argv[fn],"r");
                    for(ln=0;fgets(read,LINE_LENGTH,open); ln++) {
                            fprintf(files[ln],"%s",read);
                    }
                    fclose(open);
            }
            for(ln=0;ln<ftot;ln++) {
                    fclose(files[ln]);
            }
            return 0;
    }
于 2012-12-07T20:56:53.830 に答える