sysread
和tr/\n//
。我想尝试一些其他方法来查看tr/\n//
有多快,以及尝试处理不同平均行长的文件。我创建了一个基准测试来尝试各种方法。我在Mac OS X 10.5.8上运行这个程序,并在MacBook Air上使用Perl 5.10.1:
- 调用
wc
(除了短行之外最快) tr/\n//
(下一个最快,除了长平均行长度)s/\n//g
(通常很快)while( <$fh> ) { $count++ }
(几乎总是很慢,除非tr///
卡住了)1 while( <$fh> ); $.
(非常快)
让我们忽略那个wc
,即使加上所有IPC的东西也会得到一些吸引人的数字。
乍一看,当行长很短(比如100个字符)时,tr/\n//
看起来非常好,但是它的性能会在行长变大(比如一行有1000个字符)时下降。行长越长,tr/\n//
的表现就越差。我的基准测试有问题吗?还是内部发生了其他问题导致tr///
性能下降?为什么s///
没有类似的性能下降呢?
首先,看一下结果:
Rate very_long_lines-tr very_long_lines-$count very_long_lines-$. very_long_lines-s very_long_lines-wc
very_long_lines-tr 1.60/s -- -10% -12% -39% -72%
very_long_lines-$count 1.78/s 11% -- -2% -32% -69%
very_long_lines-$. 1.82/s 13% 2% -- -31% -68%
very_long_lines-s 2.64/s 64% 48% 45% -- -54%
very_long_lines-wc 5.67/s 253% 218% 212% 115% --
Rate long_lines-tr long_lines-$count long_lines-$. long_lines-s long_lines-wc
long_lines-tr 9.56/s -- -5% -7% -30% -63%
long_lines-$count 10.0/s 5% -- -2% -27% -61%
long_lines-$. 10.2/s 7% 2% -- -25% -60%
long_lines-s 13.6/s 43% 36% 33% -- -47%
long_lines-wc 25.6/s 168% 156% 150% 88% --
Rate short_lines-$count short_lines-s short_lines-$. short_lines-wc short_lines-tr
short_lines-$count 60.2/s -- -7% -11% -34% -42%
short_lines-s 64.5/s 7% -- -5% -30% -38%
short_lines-$. 67.6/s 12% 5% -- -26% -35%
short_lines-wc 91.7/s 52% 42% 36% -- -12%
short_lines-tr 104/s 73% 61% 54% 14% --
Rate varied_lines-$count varied_lines-s varied_lines-$. varied_lines-tr varied_lines-wc
varied_lines-$count 48.8/s -- -6% -8% -29% -36%
varied_lines-s 51.8/s 6% -- -2% -24% -32%
varied_lines-$. 52.9/s 8% 2% -- -23% -30%
varied_lines-tr 68.5/s 40% 32% 29% -- -10%
varied_lines-wc 75.8/s 55% 46% 43% 11% --
以下是基准测试代码。虽然其中有控制代码,但速度非常快,因此我并不关注它。第一次运行时,基准测试会创建测试文件并打印有关其行长度的一些统计信息:
use Benchmark qw(cmpthese);
use Statistics::Descriptive;
my @files = create_files();
open my( $outfh ), '>', 'bench-out';
foreach my $file ( @files )
{
cmpthese(
100, {
# "$file-io-control" => sub {
# open my( $fh ), '<', $file;
# print "Control found 99999 lines\n";
# },
"$file-\$count" => sub {
open my( $fh ), '<', $file;
my $count = 0;
while(<$fh>) { $count++ }
print $outfh "\$count found $count lines\n";
},
"$file-\$." => sub {
open my( $fh ), '<', $file;
1 while(<$fh>);
print $outfh "\$. found $. lines\n";
},
"$file-tr" => sub {
open my( $fh ), '<', $file;
my $lines = 0;
my $buffer;
while (sysread $fh, $buffer, 4096) {
$lines += ($buffer =~ tr/\n//);
}
print $outfh "tr found $lines lines \n";
},
"$file-s" => sub {
open my( $fh ), '<', $file;
my $lines = 0;
my $buffer;
while (sysread $fh, $buffer, 4096) {
$lines += ($buffer =~ s/\n//g);
}
print $outfh "s found $lines line\n";
},
"$file-wc" => sub {
my $lines = `wc -l $file`;
chomp( $lines );
print $outfh "wc found $lines line\n";
},
}
);
}
sub create_files
{
my @names;
my @files = (
[ qw( very_long_lines 10000 4000 5000 ) ],
[ qw( long_lines 10000 700 800 ) ],
[ qw( short_lines 10000 60 80 ) ],
[ qw( varied_lines 10000 10 200 ) ],
);
foreach my $tuple ( @files )
{
push @names, $tuple->[0];
next if -e $tuple->[0];
my $stats = create_file( @$tuple );
printf "%10s: %5.2f %5.f \n", $tuple->[0], $stats->mean, sqrt( $stats->variance );
}
return @names;
}
sub create_file
{
my( $name, $lines, $min, $max ) = @_;
my $stats = Statistics::Descriptive::Full->new();
open my( $fh ), '>', $name or die "Could not open $name: $!\n";
foreach ( 1 .. $lines )
{
my $line_length = $min + int rand( $max - $min );
$stats->add_data( $line_length );
print $fh 'a' x $line_length, "\n";
}
return $stats;
}
tr///
基准测试可能有太多的变量。随着行数变得更长,我们不仅仅是在测试tr///
处理长字符串的能力;我们还在修改行长度和sysread
缓冲区大小之间的相对关系。我对这个问题并不是很了解,所以也许这是一个无效的考虑。然而,如果问题是“tr///
在处理长字符串时会变慢吗?”那么基准测试应该专注于这一点,而不涉及 IO 问题。 - FMc