我想知道我们一直使用的基准测试是否有太多的移动部分:我们正在处理不同大小的数据文件,使用不同的行长度,并尝试衡量速度tr
相对于其竞争对手——有一个基本的(但未经测试的)假设:tr
是其性能随线路长度而变化的方法。
另外,正如布莱恩在一些评论中指出的那样,我们正在喂养tr
数据缓冲区始终大小相同(4096 字节)。如果任何方法应该是不敏感的对于线条尺寸,应该是tr
.
然后我突然想到:如果tr
参考点是否稳定,其他方法是否随线尺寸变化?当你向宇宙飞船的窗户望去时,是你还是那只克林贡猛禽在移动?
因此,我开发了一个保持数据文件大小不变的基准:行长度变化,但字节总数保持不变。结果显示:
-
tr
是最不敏感的方法
线长度的变化。自从
处理的总字节数为
所有三个线长度均为常数
经过测试(短、中、长),这个
意思是tr
是相当有效的
编辑给定的字符串。甚至
虽然短行数据文件
需要更多的编辑,tr
方法能够处理数据
文件几乎和处理的速度一样快
长行文件。
- 依赖的方法
<>
速度
随着队伍变长,
尽管速度在递减。这
有道理:因为每次调用<>
需要一些工作,应该是
处理给定的 N 个字节的速度较慢
使用较短的线路(至少超过
测试范围)。
- The
s///
方法也很敏感
到线长。喜欢tr
, 这
方法通过编辑字符串来工作
它是给定的。再次,较短的线路
长度意味着更多的编辑。显然,
的能力s///
做出这样的
编辑效率比
的那个tr
.
以下是使用 Perl 5.8.8 在 Solaris 上的结果:
# ln = $. <>, then check $.
# nn = $n <>, counting lines
# tr = tr/// using sysread
# ss = s/// using sysread
# S = short lines (50)
# M = medium lines (500)
# L = long lines (5000)
Rate nn-S
nn-S 1.66/s --
ln-S 1.81/s 9%
ss-S 2.45/s 48%
nn-M 4.02/s 142%
ln-M 4.07/s 145%
ln-L 4.65/s 180%
nn-L 4.65/s 180%
ss-M 5.85/s 252%
ss-L 7.04/s 324%
tr-S 7.30/s 339% # tr
tr-L 7.63/s 360% # tr
tr-M 7.69/s 363% # tr
Windows ActiveState 的 Perl 5.10.0 上的结果大致相当。
最后是代码:
use strict;
use warnings;
use Set::CrossProduct;
use Benchmark qw(cmpthese);
# Args: file size (in million bytes)
# N of benchmark iterations
# true/false (whether to regenerate files)
#
# My results were run with 50 10 1
main(@ARGV);
sub main {
my ($file_size, $benchmark_n, $regenerate) = @_;
$file_size *= 1000000;
my @file_names = create_files($file_size, $regenerate);
my %methods = (
ln => \&method_ln, # $.
nn => \&method_nn, # $n
tr => \&method_tr, # tr///
ss => \&method_ss, # s///
);
my $combo_iter = Set::CrossProduct->new([ [keys %methods], \@file_names ]);
open my $log_fh, '>', 'log.txt';
my %benchmark_args = map {
my ($m, $f) = @$_;
"$m-$f" => sub { $methods{$m}->($f, $log_fh) }
} $combo_iter->combinations;
cmpthese($benchmark_n, \%benchmark_args);
close $log_fh;
}
sub create_files {
my ($file_size, $regenerate) = @_;
my %line_lengths = (
S => 50,
M => 500,
L => 5000,
);
for my $f (keys %line_lengths){
next if -f $f and not $regenerate;
create_file($f, $line_lengths{$f}, $file_size);
}
return keys %line_lengths;
}
sub create_file {
my ($file_name, $line_length, $file_size) = @_;
my $n_lines = int($file_size / $line_length);
warn "Generating $file_name with $n_lines lines\n";
my $line = 'a' x ($line_length - 1);
chop $line if $^O eq 'MSWin32';
open(my $fh, '>', $file_name) or die $!;
print $fh $line, "\n" for 1 .. $n_lines;
close $fh;
}
sub method_nn {
my ($data_file, $log_fh) = @_;
open my $data_fh, '<', $data_file;
my $n = 0;
$n ++ while <$data_fh>;
print $log_fh "$data_file \$n $n\n";
close $data_fh;
}
sub method_ln {
my ($data_file, $log_fh) = @_;
open my $data_fh, '<', $data_file;
1 while <$data_fh>;
print $log_fh "$data_file \$. $.\n";
close $data_fh;
}
sub method_tr {
my ($data_file, $log_fh) = @_;
open my $data_fh, '<', $data_file;
my $n = 0;
my $buffer;
while (sysread $data_fh, $buffer, 4096) {
$n += ($buffer =~ tr/\n//);
}
print $log_fh "$data_file tr $n\n";
close $data_fh;
}
sub method_ss {
my ($data_file, $log_fh) = @_;
open my $data_fh, '<', $data_file;
my $n = 0;
my $buffer;
while (sysread $data_fh, $buffer, 4096) {
$n += ($buffer =~ s/\n//g);
}
print $log_fh "$data_file s/ $n\n";
close $data_fh;
}
更新以回应布拉德的评论。我尝试了所有三种变体,它们的行为大致如下s/\n//g
-- 行数较短的数据文件速度较慢(附加条件是s/(\n)/$1/
甚至比其他人还慢)。有趣的是m/\n/g
速度基本相同s/\n//g
,表明正则表达式方法的缓慢(两者s///
and m//
)并不直接取决于以下问题editing字符串。