2009-05-01 23 views
13

Tengo una secuencia de comandos Perl que procesa una gran cantidad de datos. Hay un montón de variables de cadena que comienzan pequeñas pero crecen mucho debido al uso repetido del operador de punto (concatenación). ¿El crecimiento de la cuerda de esta manera dará como resultado reasignaciones repetidas? En caso afirmativo, ¿hay alguna manera de preasignar una cadena?¿Cómo puedo preasignar una cadena en Perl?

Respuesta

7

Sugerencia alternativa que será mucho más fácil de manejar: push las cadenas en una matriz y join cuando haya terminado.

+7

Aunque cada elemento de la matriz crea un SV con todos sus gastos generales. Usará mucha más memoria de esta manera. –

-2

Sí, las cuerdas preextendidas que usted sabe que crecerán son una buena idea.

Puede usar el operador 'x' para hacer esto. Por ejemplo, para asignar previamente: 1000 plazas

$ s = "" x 1000:

+0

Y luego use substr en el lhs de las asignaciones. Uuuuugly. – chaos

+0

Mientras que esto creará una cadena que contiene 1000 plazas, cuando luego decir "$ s = 'foo'", voy a recibir una cadena de 1.000 caracteres con sólo los tres primeros utilizado o va a darme una nueva cadena de 3 caracteres y ¿tirar el tuyo? (Sospecho que este último, pero en realidad no sé cómo lo manejarán Perl.) –

+1

Si reasigna él, se tire el resultado de edad (suponiendo distancia referencias a ella). Tendría que hacer un reemplazo de cadena, como dijo Dave, para modificar solo partes de ella. ++ array-then-join – Anonymous

7

cadenas de Perl son mutables, por lo que anexar a una cadena hace NO incurrir en una penalización cadena de duplicación.

Puede probar todo lo que quiera para encontrar una manera "más rápida", pero esto huele realmente mal a la optimización prematura.

Por ejemplo, aumenté una clase que abstraía el trabajo duro. Funciona perfectamente, pero es, a pesar de todos sus trucos tontos, realmente lento.

Aquí está el resultado:

  Rate magic normal 
magic 1.72/s  -- -93% 
normal 23.9/s 1289%  -- 

Sí, eso es correcto, Perl es un 1200% más rápido de lo que pensaba era un respetable aplicación.

Perfile su código y descubra cuáles son los verdaderos problemas, no intente optimizar cosas que ni siquiera son un problema conocido.

#!/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

Decir que Perl no duplica la cadena es solo la mitad de la verdad. Perl asigna solo unos pocos caracteres extra a una cadena, por lo que es probable que Perl haga crecer la memoria que contiene la cadena cuando se agrega. Esto puede causar que la memoria sea copiada. Pero esto sucede en el administrador de memoria de su sistema, que es muy rápido. Recuerde, O (n) vencerá a O (logn) en la clase de matemáticas, pero en el mundo real importa el tiempo constante del algoritmo. C es rápido. – Schwern

+0

De hecho, O (1) no es muy bueno si O (1) es de varios días de un paso, mientras que O (n^2) puede tomar sólo unos segundos :) Aunque, tal vez una ventaja si el tamaño de los datos es tan grande que el enfoque O (n^2) excede varias semanas y que el conjunto de datos de tamaño es común. –

15

Sí, Perl crecer una cadena dará lugar a reasignaciones repetidas. Perl asigna un poco de espacio extra a las cadenas, pero solo unos pocos bytes. Puedes ver esto usando Devel :: Peek. Esta reasignación es muy rápida y, a menudo, no copia realmente la memoria. Confíe en su administrador de memoria, esa es la razón por la que está programando en Perl y no en C. ¡Haga una evaluación comparativa primero!

Puede preasignar matrices con $#array = $num_entries y un hash con keys %hash = $num_keys pero length $string = $strlen no funciona. Aquí hay un clever trick I dug up on Perlmonks.

my $str = ""; 
vec($str, $length, 8)=0; 
$str = ""; 

O si lo desea conseguir en XS puede llamar SvGROW().

La sugerencia de caos de utilizar un conjunto y luego unirlo todo utilizará más del doble de la memoria. Memoria para la matriz. Memoria para cada escalar asignado para cada elemento en la matriz. Memoria para la cadena sostenida en cada elemento escalar. Memoria para la copia cuando se une. Si resulta en un código más simple, hágalo, pero no crea que está guardando memoria.

0

me gustaría ir la matriz/unirse manera:

push(@array, $crunched_bit) 

Y luego $str = join('', @array), si nada más, para tener acceso a todos los elementos para la depuración en algún momento posterior.

+0

Esto consumirá una gran cantidad de memoria extra ya que cada elemento de la matriz necesita un nuevo SV. –

3

No sé específicamente cómo se implementan las cadenas de Perl, pero una muy buena suposición es que es constant amortized time. Esto significa que incluso si usted encuentra una manera de asignar previamente sus posibilidades de cuerdas son que el tiempo combinado que ahorrará para todos los usuarios del guión será menor que el tiempo que pasó pidiendo this question desbordamiento de pila.

Cuestiones relacionadas