1

これは以前は正常に動作していたスクリプトですが、現在は膨大な数の i ノード (約 400K) を扱っているため、I/O が遅くなるようです。スクリプトは、識別子のリストである定義ファイル「def」を読み取り、「dir」ディレクトリ内の 400K ファイルのそれぞれについて、最初の 4 行で識別子の 1 つが見つかった場合、ファイルの内容全体を追加します。 「def」固有のファイルの終わり。

#!/bin/sh
for def in *.def
do
        touch $def.out
        for file in $dir/*
        do
                if head -4 $file | grep -q -f  $def
                then
                        cat $file >> $def.out
                fi
        done
done

どうすればもっと速くできますか?

4

3 に答える 3

2

パールソリューション。スクリプトよりもはるかに高速である必要があります。

  1. 各 .def ファイルから正規表現を作成します。各 .def ファイルを複数回読み取ることはありません。
  2. opendirディレクトリの内容を読み取るために使用します。globを実行するよりもはるかに高速です*が、ペナルティとして、ファイルはソートされません。あなたと私のスクリプトの出力を比較するには、使用する必要があります

    diff <(sort $def.out) <(sort $def-new.out)
    

    opendirを aに置き換えると、globまったく同じ出力が得られます。スクリプトの速度は遅くなりますが、それでも古いスクリプトよりもはるかに高速です。

スクリプトは次のとおりです。

#!/usr/bin/perl
use warnings;
use strict;

my $dir = 'd';              # Enter your dir here.

my @regexen;
my @defs = glob '*.def';
for my $def (@defs) {
    open my $DEF,   '<', $def           or die "$def: $!";
    open my $TOUCH, '>', "$def-new.out" or die "$def-new.out: $!";
    my $regex = q();
    while (<$DEF>) {
        chomp;
        $regex .= "$_|"
    }
    substr $regex, -1, 1, q();
    push @regexen, qr/$regex/;
}

# If you want the same order, uncomment the following 2 lines and comment the next 2 ones.
#
# for my $file (glob "$dir/*") {
#     $file =~ s%.*/%%;

opendir my $DIR, $dir or die "$dir: $!";
while (my $file = readdir $DIR) {
    next unless -f "$dir/$file";

    my %matching_files;
    open my $FH, '<', "$dir/$file" or die "$dir/$file: $!";
    while (my $line = <$FH>) {
        last if $. > 4;
        my @matches = map $line =~ /$_/ ? 1 : 0, @regexen;
        $matching_files{$_}++ for grep $matches[$_], 0 .. $#defs;
    }

    for my $i (keys %matching_files) {
        open my $OUT, '>>', "$defs[$i]-new.out" or die "$defs[$i]-new.out: $!";
        open my $IN,  '<',  "$dir/$file"        or die "$dir/$file: $!";
        print $OUT $_ while <$IN>;
        close $OUT;
    }
}

更新しました

ファイルを数回フェッチできるようになりました。1 つの巨大な正規表現を作成する代わりに、正規表現の配列が作成され、1 つずつ照合されます。

于 2013-03-05T00:31:26.453 に答える
1

1 つのフォルダーに 10,000 を超えるファイルがあると、パフォーマンスの問題が発生し始めることがわかりました。その場合、lsコマンドでさえ戻るのに数秒かかることがあります。

あなたのスクリプトは本質的に IO が重いようです。多くのファイルを見て、多くのファイルを作成または追加しています。スクリプトの動作を変更せずに改善できるものは何もありません。

可能であれば、このデータの一部をデータベースに移動してください。データベースは、ファイルシステムよりも簡単にこの規模のデータに合わせて調整できます。

于 2013-03-05T00:43:05.657 に答える
0

あなたはたくさんのフォークを節約することができます。ループに保存された1つのフォークは、スクリプト全体で合計400Kのフォークになります。これが私がすることです。

* .defごとにタッチする代わりに、大きなチャンクでタッチします。

find . -name '*.def' | sed 's/\(.*\)/\1.out/' | xargs touch

(検索結果がそれをサポートしている場合は、find . -maxdepth 1...を使用してください)

2つのコマンドパイプの代わりに、1つのコマンドで実行します。

if awk "NR <= 4 && /$def/ { exit 0 } NR==5 { exit 1 }" $file; then

(ただし、メタ文字が含まれていない場合は$ defを確認してください。ドットは問題ありません。)

于 2013-03-05T12:21:43.493 に答える