【发布时间】:2010-10-23 06:15:43
【问题描述】:
我有一个处理大量数据的 Perl 脚本。由于重复使用点(连接)运算符,有一堆字符串变量开始很小,但会变得很长。以这种方式增长字符串会导致重复重新分配吗?如果是,有没有办法预先分配一个字符串?
【问题讨论】:
我有一个处理大量数据的 Perl 脚本。由于重复使用点(连接)运算符,有一堆字符串变量开始很小,但会变得很长。以这种方式增长字符串会导致重复重新分配吗?如果是,有没有办法预先分配一个字符串?
【问题讨论】:
是的,Perl 增长字符串会导致重复的重新分配。 Perl 为字符串分配了一点额外空间,但只有几个字节。您可以使用 Devel::Peek 看到这一点。这种重新分配非常快,并且通常不会实际复制内存。相信您的内存管理器,这就是您使用 Perl 而不是 C 编程的原因。首先对其进行基准测试!
您可以使用$#array = $num_entries 和keys %hash = $num_keys 预分配数组,但length $string = $strlen 不起作用。这是clever trick I dug up on Perlmonks。
my $str = "";
vec($str, $length, 8)=0;
$str = "";
或者如果你想进入 XS,你可以拨打SvGROW()。
chaos 建议使用一个数组,然后将它们全部连接在一起将使用两倍以上的内存。数组的内存。为数组中的每个元素分配的每个标量的内存。每个标量元素中保存的字符串的内存。加入时副本的内存。如果它导致代码更简单,那就去做吧,但不要认为你在节省任何内存。
【讨论】:
Perl 的字符串是可变的,所以附加到一个字符串 NOT 会导致字符串重复惩罚。
你可以尝试所有你想找到“更快”的方法,但这闻起来很不利于过早的优化。
举个例子,我创建了一个抽象出繁重工作的课程。它工作得很好,但它的所有愚蠢的技巧,真的很慢。
结果如下:
Rate magic normal
magic 1.72/s -- -93%
normal 23.9/s 1289% --
是的,没错,Perl 比我认为值得尊敬的实现快 1200%。
分析您的代码并找出真正的问题所在,不要尝试优化甚至不是已知问题的内容。
#!/usr/bin/perl
use strict;
use warnings;
{
package MagicString;
use Moose;
has _buffer => (
isa => 'Str',
is => 'rw',
);
has _buffer_size => (
isa => 'Int',
is => 'rw',
default => 0,
);
has step_size => (
isa => 'Int',
is => 'rw',
default => 32768,
);
has _tail_pos => (
isa => 'Int',
is => 'rw',
default => 0,
);
sub BUILD {
my $self = shift;
$self->_buffer( chr(0) x $self->step_size );
}
sub value {
my $self = shift;
return substr( $self->{buffer}, 0, $self->{_tail_pos} );
}
sub append {
my $self = shift;
my $value = shift;
my $L = length($value);
if ( ( $self->{_tail_pos} + $L ) > $self->{_buffer_size } ){
$self->{buffer} .= (chr(0) x $self->{step_size} );
$self->{_buffer_size} += $self->{step_size};
}
substr( $self->{buffer}, $self->{_tail_pos}, $L, $value );
$self->{_tail_pos} += $L;
}
__PACKAGE__->meta->make_immutable;
}
use Benchmark qw( :all :hireswallclock );
cmpthese( -10 , {
magic => sub{
my $x = MagicString->new();
for ( 1 .. 200001 ){
$x->append( "hello");
}
my $y = $x->value();
},
normal =>sub{
my $x = '';
for ( 1 .. 200001 ){
$x .= 'hello';
}
my $y = $x;
}
});
#use Data::Dumper;
#print Dumper( length( $x->value() ));
【讨论】:
我不具体知道 Perl 字符串是如何实现的,但一个很好的猜测是它是 constant amortized time。这意味着即使您确实找到了预分配字符串的方法,它为所有脚本用户节省的总时间也将少于您在 Stack Overflow 上询问 this question 所花费的时间。
【讨论】:
我会采用数组/加入方式:
push(@array, $crunched_bit)
然后$str = join('', @array),如果仅此而已,可以访问所有元素以便稍后进行调试。
【讨论】:
是的,您知道会增长的预扩展字符串是个好主意。
您可以使用“x”运算符来执行此操作。例如,预分配 1000 个空间:
$s = " " x 1000:
【讨论】:
$s = " " x 1000; $s = ""; 将预分配 1000+ 个字节给 $s 并为您留下一个空字符串。然而,Perl 必须计算 " " x 1000 这有点浪费。 vec() 解决方案更好。或者干脆别担心。