我相信脚本中的性能瓶颈根本不是内存使用,而是您为每条记录打开和关闭一个文件。如果我正确理解了单位,100 万是 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 位键。结果输出写入 100,000 个00000.csv
以99999.csv
. 原始脚本运行大约需要 3 分钟,而我上面的脚本(批量大小为 1000000)大约需要 1:26,因此速度大约是原来的两倍。
瓶颈不是脚本本身,而是文件系统操作。创建/更新 100,000 个文件本质上是昂贵的。