我这里有2个文件,分别是newFile和LookupFile(大文件)。 将在LookupFile中搜索newFile中的内容并进行进一步处理。这个脚本运行正常,但执行时间更长。你能告诉我在这里可以做些什么来提高性能吗?如果我们可以将文件转换为哈希以提高性能,请告诉我吗?
我的文件如下所示
NewFile和LookupFile:
acl sourceipaddress subnet destinationipaddress subnet portnumber 。 。
脚本:
#!/usr/bin/perl
use strict;
use warnings;
use File::Slurp::Tiny 'read_file';
use File::Copy;
use Data::Dumper;
use File::Copy qw(copy);
my %options = (
LookupFile => {
type => "=s",
help => "File name",
variable => 'gitFile',
required => 1,
}, newFile => {
type => "=s",
help => "file containing the acl lines to checked for",
variable => ‘newFile’,
required => 1,
} );
$opts->addOptions(%options);
$opts->parse();
$opts->validate();
my $newFile = $opts->getOption('newFile');
my $LookupFile = $opts->getOption('LookupFile');
my @LookupFile = read_file ("$LookupFile");
my @newFile = read_file ("$newFile");
@LookupFile = split (/\n/,$LookupFile[0]);
@newLines = split (/\n/,$newFile[0]);
open FILE1, "$newFile" or die "Could not open file: $! \n";
while(my $line = <FILE1>)
{
chomp($line);
my @columns = split(' ',$line);
$var = @columns;
my $fld1;
my $cnt;
my $fld2;
my $fld3;
my $fld4;
my $fld5;
my $dIP;
my $sIP;
my $sHOST;
my $dHOST;
if(....)
if (....) further checks and processing
)
答案 0 :(得分:2)
在进行任何优化之前要做的第一件事就是分析您的代码。这不会猜测,而是告诉你哪些线占用的时间最多,以及它们被调用的次数。 Devel::NYTProf是这项工作的好工具。
这是一个问题。
my @LookupFile = read_file ("$LookupFile");
my @newFile = read_file ("$newFile");
@LookupFile = split (/\n/,$LookupFile[0]);
@newLines = split (/\n/,$newFile[0]);
read_file
将整个文件作为一个大字符串读取(它应该是my $contents = read_file(...)
,使用数组很尴尬)。然后它将整个事物分成新行,复制文件中的所有内容。这种记忆非常缓慢而且不必要。
相反,请使用read_lines
。这会将文件拆分成行,因为它可以避免代价高昂的副本。
my @lookups = read_lines($LookupFile);
my @new = read_lines($newFile);
下一个问题是$newFile
再次打开并逐行迭代。
open FILE1, "$newFile" or die "Could not open file: $! \n";
while(my $line = <FILE1>) {
这已经浪费了,因为您已经将该文件读入内存。使用其中一个。但是,一般来说,最好逐行处理文件,而不是将它们全部插入内存。
上述情况会加快速度,但他们并没有解决问题的症结所在。这可能是真正的问题......
将在LookupFile中搜索newFile中的内容并进行进一步处理。
你没有表现出你在做什么,但我会想象它看起来像这样......
for my $line (@lines) {
for my $thing (@lookups) {
...
}
}
也就是说,对于一个文件中的每一行,您都在查看另一行中的每一行。这就是所谓的O(n ^ 2)算法,这意味着当您将文件大小加倍时,您的时间将翻两番。
如果每个文件有10行,则内循环将需要100(10 ^ 2)次转动。如果他们有100行,则需要10,000(100 ^ 2)。拥有1,000条线路需要1,000,000次。
随着尺寸越大,O(n ^ 2)越快,速度越慢。
如果我们可以将文件转换为哈希以提高性能,请告诉我吗?
你有正确的想法。您可以将查找文件转换为哈希以加快速度。让我们说它们都是单词列表。
# input
foo
bar
biff
up
down
# lookup
foo
bar
baz
您想检查input
中的任何行是否与lookup
中的任何行匹配。
首先,您已阅读lookup
并将其转换为哈希值。然后你会阅读input
并检查每一行是否在哈希值中。
use strict;
use warnings;
use autodie;
use v5.10;
...
# Populate `%lookup`
my %lookup;
{
open my $fh, $lookupFile;
while(my $line = <$fh>) {
chomp $line;
$lookup{$line} = 1;
}
}
# Check if any lines are in %lookup
open my $fh, $inputFile;
while(my $line = <$fh>) {
chomp $line;
print $line if $lookup{$line};
}
这样您只需遍历每个文件一次。这是一种O(n)算法,意思是线性扩展,因为散列查找基本上是瞬时的。如果每个文件有10行,则每个循环只需要10次迭代。如果它们有100行,则每个循环只需要100次迭代。 1000行,1000次迭代。
最后,您真正想要做的是跳过所有这些并为您的数据创建数据库并进行搜索。 SQLite是一个SQL数据库,不需要服务器,只需要一个文件。将数据放入其中并使用DBD::SQLite对其执行SQL查询。
虽然这意味着您必须学习SQL,并且构建和维护数据库需要付出代价,但这很快且最重要的是非常灵活。 SQLite可以快速完成各种搜索,而无需编写大量额外代码。 SQL数据库非常常见,因此学习SQL是一项非常好的投资。
由于你用my @columns = split(' ',$line);
分割文件,它可能是一个包含许多字段的文件。这很可能会很好地映射到SQL表。
SQLite甚至可以为您导入类似的文件。有关如何执行此操作的详细信息,请参阅this answer。