如何在Perl中预先分配字符串?

时间:2009-05-01 23:57:39

标签: perl string

我有一个Perl脚本可以处理大量数据。由于重复使用点(连接)运算符,有许多字符串变量从小开始但变长很长。以这种方式增长字符串会导致重复重新分配吗?如果是,是否有预先分配字符串的方法?

6 个答案:

答案 0 :(得分:15)

是的,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'建议使用数组然后将它们连接在一起将使用超过两倍的内存。阵列的内存。为数组中的每个元素分配的每个标量的内存。每个标量元素中保存的字符串的内存。加入时的副本内存。如果它导致更简单的代码,那就去做,但不要以为你在节省任何内存。

答案 1 :(得分:7)

替代建议可以更轻松地应对:push字符串到数组上,join完成后。

答案 2 :(得分:7)

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() ));

答案 3 :(得分:3)

我不知道具体如何实现Perl字符串,但很好的猜测是它是constant amortized time。这意味着,即使您确实找到了预先分配字符串的方法,但是它为所有脚本用户保存的合并时间将少于您在Stack Overflow上询问this question所花费的时间。

答案 4 :(得分:0)

我会采用数组/加入方式:

push(@array, $crunched_bit)

然后$str = join('', @array),如果没有更多,可以在以后访问所有元素以进行调试。

答案 5 :(得分:-2)

是的,预先扩展你知道会增长的字符串是一个好主意。

您可以使用'x'运算符执行此操作。例如,要预分配1000个空格:

$ s =“”x 1000: