我想知道是否有更有效的方式来做我正在尝试的事情。我需要读取文件并将文件的每一行与其后的所有文件进行比较。 (即将第1行与第2,3,4,5行比较......;第2行与3,4,5,...;第3行与4,5 ......等)。我觉得有些文件太大而无法完全读入@lists,所以我现在通过打开同一个文件的两个文件句柄并使用tell和seek来获取一个文件的位置并设置其他内容的位置来实现这一点像这样:
open FH1, '/arf/test.txt';
open FH2, '/arf/test.txt';
while ($outer = <FH1>){
chomp($outer);
$fh1Posn = tell FH1;
seek FH2, $fh1Posn, 0;
while ($inner = <FH2>){
[code to compare line];
}
}
这在小文件中工作得非常好,但是我需要处理一些较大的文件(即使我将伪代码替换为比较部分)。也许这是可以预料的,但我想知道是否有什么我可以尝试加速文件的阅读速度?我只想使用单个文件句柄,消除第二个并使用while循环底部的$ fh1Posn将光标重置回顶部的位置,如:
open FH1, '/arf/test.txt';
while ($outer = <FH1>){
chomp($outer);
$fh1Posn = tell FH1;
while ($inner = <FH1>){
[code to compare line];
}
seek FH1, $fh1Posn, 0;
}
Haven还试过这个 - 会这样做 - 但觉得它可能不会做任何有价值的事情。
答案 0 :(得分:1)
最快的解决方案是完全在内存中完成,将磁盘读取限制为一次通过。如果你没有足够的内存来做到这一点,那么我建议将它分成块。
你当前的解决方案是读取文件O(n ^ 2)次是最糟糕的解决方案,内存不足。如果以1000行为一组进行,则可以大大减少磁盘操作。你必须尝试找到你实际内存限制的位置。
my $buffersize = 1000;
open FH1, '/arf/test.txt';
open FH2, '/arf/test.txt';
while (! eof(FH1)) {
my @buffer = ();
for (1..$buffersize) {
my $outer = <FH1>;
chomp $outer;
push @buffer, $outer;
last if eof(FH1);
}
# Seek code here
while ($inner = <FH2>){
for (@buffer) {
[code to compare line];
}
}
}
2月25日星期五的附录 - 更详细的代码解决方案
我在一天结束时有一些额外的时间,所以我编写了一个版本的这个版本适用于名为test.txt的文件,其中包含每行1-10的数字。因此,我将buffersize配置为4,以便脚本执行4次传递而不是11次,就像您的原始方法一样。
use strict;
use warnings;
use autodie;
my $buffersize = 4; # 1000;
my $file = 'test.txt'; # '/arf/test.txt';
open my $fh1, '<', $file;
open my $fh2, '<', $file;
while (! eof($fh1)) {
# Move fh2 to start of buffer
my $startofbuffer = tell($fh1);
seek($fh2, $startofbuffer, 0);
# Build Buffer of entries to compare
my @buffer = ();
for (1..$buffersize) {
chomp(my $outer = <$fh1>);
print "buffer, $.\n";
push @buffer, $outer;
last if eof($fh1);
}
# Keep track of relative offset to start of buffer
for (my $offset = 0; !eof($fh2); $offset++) {
chomp(my $inner = <$fh2>);
for my $i (0..$#buffer) {
last if $i >= $offset;
my $outer = $buffer[$i];
print "Compare outer $outer to inner $inner\n";
}
}
}
输出如下
buffer, 1
buffer, 2
buffer, 3
buffer, 4
Compare outer 1 to inner 2
Compare outer 1 to inner 3
Compare outer 2 to inner 3
Compare outer 1 to inner 4
Compare outer 2 to inner 4
Compare outer 3 to inner 4
Compare outer 1 to inner 5
Compare outer 2 to inner 5
Compare outer 3 to inner 5
Compare outer 4 to inner 5
Compare outer 1 to inner 6
Compare outer 2 to inner 6
Compare outer 3 to inner 6
Compare outer 4 to inner 6
Compare outer 1 to inner 7
Compare outer 2 to inner 7
Compare outer 3 to inner 7
Compare outer 4 to inner 7
Compare outer 1 to inner 8
Compare outer 2 to inner 8
Compare outer 3 to inner 8
Compare outer 4 to inner 8
Compare outer 1 to inner 9
Compare outer 2 to inner 9
Compare outer 3 to inner 9
Compare outer 4 to inner 9
Compare outer 1 to inner 10
Compare outer 2 to inner 10
Compare outer 3 to inner 10
Compare outer 4 to inner 10
buffer, 5
buffer, 6
buffer, 7
buffer, 8
Compare outer 5 to inner 6
Compare outer 5 to inner 7
Compare outer 6 to inner 7
Compare outer 5 to inner 8
Compare outer 6 to inner 8
Compare outer 7 to inner 8
Compare outer 5 to inner 9
Compare outer 6 to inner 9
Compare outer 7 to inner 9
Compare outer 8 to inner 9
Compare outer 5 to inner 10
Compare outer 6 to inner 10
Compare outer 7 to inner 10
Compare outer 8 to inner 10
buffer, 9
buffer, 10
Compare outer 9 to inner 10
3月2日星期日的附录 - 基准
你的速度没有提高的报告让我有点好奇,所以我创建了一个小脚本来制作一些假数据:
use strict;
use warnings;
use autodie;
my $lines = shift or die "Missing line count\n";
die "Lines outside of range 1 - 1,000,000" if $lines < 1 or $lines > 1_000_000;
my $fakedata = 70;
my $filename = 'fd' . sprintf("%06d", $lines) . '.txt';
open my $fh, '>', $filename;
for my $i (1..$lines) {
my $fake = join '', map {('a'..'z')[int rand 26]} (1..$fakedata);
$fh->print("$i fake${fake}fake\n");
}
close $fh;
print "Created $filename\n";
1;
__END__
然后我编辑了上面提供的详细代码,以便它不输出任何调试声明,而是进行了非常基础的比较。对于行数为1_000,10_000和20_000的文件,这会产生以下结果。
For Buffer size, show Time in sec and (# of File Reads) -------------------------------------------------------- File by lines b = 1 b = 10 b = 100 b = 1k b = 10k ------------- ----- ------ ------- ------ ------- Lines = 1k t = 1.54s t = 0.35s t = 0.22s t = 0.21 Size = 88k r = (1001) r = (101) r = (11) r = (2) Tests = 500k Lines = 10k t = 185s t = 35s t = 23s t = 21.7s t = 21.5s Size = 899k r = (10k) r = (1k) r = (101) r = (11) r = (2) Tests = 50m Lines = 20k t = 593s t = 136s t = 90s t = 86s t = 85.5s Size = 1.8m r = (20k) r = (2k) r = (201) r = (21) r = (3) Tests = 200m
正如您所看到的,缓冲甚至只有100,使脚本时间缩短了5或更多。这些脚本仍然需要很长时间,因为比较的数量等于N * N / 2.这是针对20k文件的2亿次测试,这就是为什么你可以看到执行该文件所花费的时间是4倍的原因。只要10k文件。
如果这些值保持为真,则250k长文件将比20k文件长156.25倍。在具有缓冲的最佳情况下,这相当于3.71小时,或者在没有缓冲的情况下相当于25.7小时。这些数字甚至假设比较的绝对最短时间,我认为你的数字可能比简单的旧/偶数测试更复杂。
不幸的是,你没有说出你的项目的性质和这些比较,所以我无法猜测其他可能的速度改进。如果我假设你的目标更多是排序的本质,那么就可以将你的比较减少到O(n log n)而不是O(N ** 2)。为了排序,我建议你将文件分成能够适应内存的组,使用perl排序对它们进行排序,然后使用合并排序来合并已排序的组。我不打算提供更详细的代码,因为它只是猜想你正在做的事情。
无论如何,祝你的项目好运。