0

10 万ルピーを超えるデータを含む CSV ファイルがあります。binary::tree少ないメモリ使用量で使用したい。

このプログラムの主な用途は、最初の 5 桁を検索し、同じ最初の 5 桁のデータを格納するために新しいファイル (ファイル名は最初の 5 桁である必要があります) を作成します。

私のコードは正常に動作していますが、メモリを大量に使用しています。

このコードを使用して今書いてください:

my $file = "my_csv_file.csv";

open (my $data, '<', $file) or die "Could not open '$file' $!\n";

while (my $lines = <$data>) {
        my @fields = split "," , $lines unless $. == 1;

        my $first_five = substr ($fields[1],  0,  5,);

        if (-e "$first_five.csv" ) {
            open my $fh, '>>', "$first_five.csv" or die $!;
            print { $fh } $lines;

        }       else {
            open my $fh, '>>', "$first_five.csv" or die $!;
            print $fh "Title\n";
        }               
        close $fh;
}
 close $data;
4

1 に答える 1

0

スクリプトのパフォーマンスのボトルネックは、メモリの使用ではなく、レコードごとにファイルを開いたり閉じたりすることだと思います。単位を正しく理解していれば、10lakh は 1,000,000 です。

1 つの解決策は、データをバッチで処理することです。特に、キーとして抽出する「最初の 5」に多くのキーが繰り返される場合はそうです。

その 2 番目のフィールドに 100 個の一意の 5 桁のキーを含むが、10,000,000 レコード (ファイルのサイズの 10 倍) を含む合成ファイルで、以下のプログラムに対してあなたのプログラムをベンチマークしました。行は次のようになりました。

1,9990001,----------------------------------------------------------------------------------------------------
2,9990002,----------------------------------------------------------------------------------------------------
3,9990003,----------------------------------------------------------------------------------------------------

これは、入力内の適度に大量のデータをシミュレートするために行いました。入力ファイルのレコード数の約 10 倍になるはずです。

あなたの元のスクリプトは、私のコンピューターでこの入力を処理するのに 2 分以上かかりました。次のスクリプトは、10,000 レコードのバッチを使用しており、24 秒かかりました。5倍以上の速さです。

my $file = "my_csv_file.csv";

open (my $data, '<', $file) or die "Could not open '$file' $!\n";

sub write_out
{
    my $batch = shift;

    for my $first_five (keys %$batch)
    {
        my $file_name  = $first_five . ".csv";
        my $need_title = ! -e $file_name;

        open my $fh, '>>', $file_name or die $!;
        print $fh "Title\n" if $need_title;
        print $fh @{ $batch->{$first_five} };
        close $fh;
    }
}

my ($line, $batch, $count);

$batch = { };
$count = 0;

while ($line = <$data>)
{
    next if $. == 1;

    if ($line =~ /^[^,]*,(.....)/)
    {
        push @{ $batch->{$1} }, $line;

        if (++$count > 10000)   # adjust as needed
        {
            write_out $batch;
            $batch = { };
            $count = 0;
        }
    }
}

write_out $batch if $count; # write final batch

close $data;

ここで、私のスクリプトの出力とあなたのスクリプトの出力の間に 1 つの違いがあることに気付きました。あなたのスクリプトは、出力先の .csv ファイルごとに出力の最初の行を削除しTitle、その場所に単語を配置しているようです。エラーだったと思います。上記のスクリプトは、指定された「最初の 5 つ」の最初のインスタンスを削除せずに、 という名前の行を追加します。Title

以前の動作が必要な場合は、 で変更できますsub write_out

いくつかの追加実験を行いました。バッチ サイズを 10,000,000 に変更して、write_out1 回だけ呼び出されるようにしました。メモリ使用量はかなり増加し、実行時間はわずか 22 秒になりました。また、バッチ サイズを 100 に変更してみました。メモリ使用量は劇的に減少しましたが、実行時間は約 30 秒になりました。これは、ファイルの開閉が真のボトルネックであることを示唆しています。

したがって、バッチ サイズを変更することで、実行時間に対するメモリ フットプリントを制御できます。いずれにせよ、バッチ指向のコードは、現在のアプローチよりもはるかに高速になるはずです。

編集: 2 番目の 1000 万レコード入力を使用してさらにベンチマークを行い、今回は 5 桁のキーを完全にランダム化しました。00000.csv結果の出力は、 ~という名前の 100,000 個のファイルを書き込みます99999.csv。元のスクリプトの実行には約 3 分かかりますが、上記のスクリプト (バッチ サイズが 1000000) は約 1:26 かかり、約 2 倍の速さです。

ボトルネックはスクリプト自体ではなく、ファイル システムの操作です。100,000 個のファイルを作成/更新するには、本質的にコストがかかります。

于 2013-08-05T14:27:19.697 に答える