我有CSV文件,数据超过10万。我想使用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;
答案 0 :(得分:0)
我认为脚本中的性能瓶颈根本不是内存使用,而是为每条记录打开和关闭文件。如果我正确地理解了单位,那么10万就是1,000,000,所以开启和关闭都很多。
一种解决方案是批量处理数据,特别是如果您提取的“前5”中有许多重复键,则为
。我在一个合成文件中对你的程序进行了基准测试,该文件在第二个字段中包含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;
现在,我注意到我的脚本输出和你的输出之间存在一个区别:你似乎放弃了每个目标.csv文件的第一行输出,将单词Title
放在其位置。我认为这是一个错误。我在上面的脚本添加一行名为Title
的行,而不删除给定“前五个”的第一个实例。
如果您想要以前的行为,可以在sub write_out
。
我做了一些额外的实验。我将批量大小更改为10,000,000,因此write_out
只会被调用一次。内存使用量确实增长了很多,运行时间只降到了22秒。我还尝试将批量大小更改为100.内存使用量急剧下降,但运行时间上升到大约30秒。这表明文件打开/关闭是真正的瓶颈。
因此,通过更改批量大小,您可以控制内存占用量与运行时间。无论如何,面向批处理的代码应该比当前的方法快得多。
编辑:我使用第二个1000万条记录输入进行了进一步的基准测试,这次完全随机化了5位数字键。生成的输出会将名为00000.csv
的100,000个文件写入99999.csv
。原始脚本大约需要3分钟才能运行,我上面的脚本(批量大小为1000000)大约需要1:26,所以大约快两倍。
瓶颈不是脚本本身,而是文件系统操作。创建/更新100,000个文件本身就很昂贵。