2

小さな 12 x 2 テキスト ファイル (約 260 バイト) と大きな .bedgraph ファイル (少なくとも 1.3 MB のサイズ) を処理する perl スクリプトを実行しようとしています。これら 2 つのファイルから、スクリプトは新しいベッドグラフ ファイルを出力します。

他の 3 つの .bedgraph ファイルでこのスクリプトを実行しましたが、プロセスが取得し続ける残りのファイルで実行しようとしましたKilled

各ファイルで perl スクリプトを実行するには、平均で約 20 分かかり.bedgraphます。

サーバーからではなく、ローカル マシンで perl スクリプトを実行しています。Linux OS Ubuntu 12.04 システム 64 ビット 4GB RAM を使用しています。

perl スクリプトの実行が強制終了され続けるのはなぜですか? どうすれば修正できますか?

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

# input file handle
open(my $sizes_fh, '<', 'S_lycopersicum_chromosomes.size') or die $!;

# output file handles
open(my $output, '+>', 'tendaysafterbreaker_output.bedgraph') or die $!;

my @array;

while(<$sizes_fh>){
    chomp;
    my ($chrom1, $size) = split(/\t/, $_);
    @array = (0) x $size;

    open(my $bedgraph_fh, '<', 'Solanum_lycopersicum_tendaysafterbreaker.bedgraph') or die $!;
    while(<$bedgraph_fh>){
        chomp;
        my ($chrom2, $start, $end, $FPKM) = split(/\t/, $_);

        if ($chrom1 eq $chrom2){
            for(my $i = $start; $i < $end; $i++){
                $array[$i] += $FPKM;
            }
        }
    }

    close $bedgraph_fh or warn $!;

    my ($last_start, $last_end) = 0;
    my $last_value = $array[0];

    for (my $i = 1; $i < $#array; $i++){
        my $curr_val = $array[$i];
        my $curr_pos = $i;

        # if the current value is not equal to the last value
        if ($curr_val != $last_value){
            my $last_value = $curr_val;
            print $output "$chrom1\t$last_start\t$last_end\t$last_value\n";
            $last_start = $last_end = $curr_pos;
        } else {
            $last_end = $i;
        }
    }
}

close $sizes_fh or warn $!;
4

3 に答える 3

5

90,000,000 要素の配列を割り当てようとしています。Perl は、その柔軟なタイピングとその他の高度な変数機能により、予想よりも多くのメモリをこれに使用します。

私の (Windows 7) マシンでは、そのような配列を割り当てるだけで他に何もしないプログラムは、3.5 GB の RAM を消費します。

この巨大なメモリ使用量を回避するには、さまざまな方法があります。ここにいくつかあります:

PDL巨大な数値配列をメモリに効率的に格納するように設計された、科学データ処理用モジュール。ただし、これは配列を割り当てて使用するための構文を変更します (そして、他のさまざまな方法で Perl の構文を台無しにします)。

DBM::Deepファイルにデータベースを割り当てるモジュールであり、通常の配列またはハッシュを介してそのデータベースにアクセスできます。

use DBM::Deep;
my @array;
my $db = tie @array, "DBM::Deep", "array.db";

#Now you can use @array like a normal array, but it will be stored in a database.
于 2013-04-26T06:33:04.833 に答える
2

C の知識が少しあれば、配列操作を低レベル コードにオフロードするのは非常に簡単です。C 配列を使用すると、必要なスペースが少なくなり、はるかに高速になります。ただし、境界チェックなどの優れた機能が失われます。Inline::C を使用した実装を次に示します。

use Inline 'C';
...;
__END__
__C__
// note: I don't know if your data contains only ints or doubles. Adjust types as needed
int array_len = -1; // last index
int *array = NULL;

void make_array(int size) {
  free(array);
  // if this fails, start checking return value of malloc for != NULL
  array = (int*) malloc(sizeof(int) * size);
  array_len = size - 1;
}

// returns false on bounds error
int array_increment(int start, int end, int fpkm) {
  if ((end - 1) > array_len) return 0;
  int i;
  for (i = start; i < end; i++) {
    array[i] += fpkm;
  }
  return 1;
}

// please check if this is actually equivalent to your code.
// I removed some unneccessary-looking variables.
void loop_over_array(char* chrom1) {
  int
    i,
    last_start = 0,
    last_end   = 0,
    last_value = array[0];
  for(i = 1; i < array_len; i++) { // are you sure not `i <= array_len`?
    if (array[i] != last_value) {
      last_value = array[i];
      // I don't know how to use Perl filehandles from C,
      // so just redirect the output on the command line
      printf("%s\t%d\t%d\t%d\n", chrom1, last_start, last_end, last_value);
      last_start = i;
    }
    last_end = i;
  }
}

void free_array {
  free(array);
}

最小限のテスト コード:

use Test::More;

make_array(15);
ok !array_increment(0, 16, 2);
make_array(95_000_000);
ok array_increment(0, 3, 1);
ok array_increment(2, 95_000_000, 1);
loop_over_array("chrom");
free_array();
done_testing;

このテスト ケースの出力は次のとおりです。

chrom   0       1       2
chrom   2       2       1

(テスト出力は削除されています)。コンパイルには数秒かかる場合がありますが、その後は非常に高速です。

于 2013-04-26T15:20:12.123 に答える
0

から読み取ったレコードで$bedgraph_fh、 の典型的な値は$start? @arrayハッシュは配列よりもエントリごとのオーバーヘッドが大きくなりますが、多くの未使用のエントリで開始すると、メモリを節約できる場合があります。たとえば、@array要素が 9000 万個あり、最初の 8000 万個がまったく使用されない場合は、ハッシュを使用したほうがよい可能性が高くなります。

それ以外に、このコードが実装するアルゴリズムで必要とされないデータを保持している明らかなケースは見当たりませんが、実際の目的によっては、そうでない代替アルゴリズムが存在する可能性がありますメモリに保持するのに必要な量のデータ。

ただし、実際に 9,000 万個のアクティブなデータ要素のセットを処理する必要がある場合、主なオプションは大量の RAM を購入するか、何らかの形式のデータベースを使用することになります。後者の場合、シンプルさと軽量さのために( DBD::SQLite経由で) SQLite を選びますが、YMMV を選びます。

于 2013-04-26T10:47:09.840 に答える