2009-05-01 35 views
13

Tôi có một tập lệnh Perl mà crunches rất nhiều dữ liệu. Có một loạt các biến chuỗi bắt đầu nhỏ nhưng phát triển thực sự lâu do việc sử dụng lặp lại của toán tử dấu chấm (concatentation). Sẽ phát triển chuỗi theo cách này dẫn đến tái phân bổ lặp đi lặp lại? Nếu có, có cách nào để phân bổ trước một chuỗi không?Làm thế nào tôi có thể phân bổ trước một chuỗi trong Perl?

Trả lời

7

Đề xuất thay thế sẽ dễ dàng hơn nhiều để đối phó với: push các chuỗi trên một mảng và join khi bạn hoàn tất.

+7

Mặc dù mọi phần tử trong mảng tạo SV với tất cả chi phí của nó. Bạn sẽ sử dụng nhiều bộ nhớ hơn theo cách này. –

-2

Có, các chuỗi mở rộng mà bạn biết sẽ phát triển là một ý tưởng hay.

Bạn có thể sử dụng toán tử 'x' để thực hiện việc này. Ví dụ, để preallocate 1000 không gian:

$ s = "" x 1000:

+0

Và sau đó sử dụng chất nền trên các lhs của bài tập. Uuuuugly. – chaos

+0

Trong khi điều này sẽ tạo một chuỗi chứa 1000 dấu cách, khi tôi nói "$ s = 'foo'", tôi có nhận được chuỗi gồm 1000 ký tự chỉ với ba ký tự đầu tiên được sử dụng hay không. ném đi? (Tôi nghi ngờ sau, nhưng không thực sự biết làm thế nào perl sẽ xử lý nó.) –

+1

Nếu bạn gán lại nó, nó sẽ vứt bỏ kết quả cũ (giả sử đi tham chiếu đến nó). Bạn sẽ cần phải thay thế chuỗi, như Dave đã nói, để sửa đổi chỉ một phần của nó. ++ array-then-join – Anonymous

7

chuỗi Perl là có thể thay đổi, vì vậy phụ thêm vào một chuỗi làm KHÔNG phải chịu một hình phạt chuỗi trùng lặp.

Bạn có thể thử tất cả những gì bạn muốn tìm một cách "nhanh hơn", nhưng điều này có mùi thực sự xấu về tối ưu hóa sớm.

Ví dụ, tôi đã đánh lừa một lớp trừu tượng hóa công việc khó khăn. Nó hoạt động hoàn hảo, nhưng nó, cho tất cả các thủ thuật ngốc nghếch của nó, thực sự chậm.

Dưới đây là kết quả:

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

Vâng, đúng vậy, Perl nhanh hơn những gì tôi nghĩ là một việc thực hiện đáng kính 1200%.

Lập hồ sơ cho mã của bạn và tìm ra vấn đề thực sự là gì, đừng thử tối ưu hóa nội dung thậm chí không phải là vấn đề đã biết.

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

Nói Perl không trùng lặp chuỗi chỉ là một nửa sự thật. Perl chỉ phân bổ thêm một vài ký tự cho một chuỗi, vì vậy Perl sẽ rất có khả năng phát triển bộ nhớ chứa chuỗi khi chắp thêm. Điều này có thể làm cho bộ nhớ được sao chép. Nhưng điều này xảy ra trong trình quản lý bộ nhớ của hệ thống của bạn rất nhanh. Hãy nhớ rằng, O (n) sẽ đánh bại O (logn) trong lớp toán, nhưng trong thế giới thực, thời gian liên tục của thuật toán là quan trọng. C nhanh. – Schwern

+0

Thật vậy, O (1) không phải là rất tốt nếu O (1) là vài ngày cho một bước, trong khi O (n^2) có thể mất vài giây :) Mặc dù, có thể là một lợi thế nếu kích thước dữ liệu của bạn quá lớn rằng cách tiếp cận O (n^2) vượt quá vài tuần và tập dữ liệu kích thước đó là phổ biến. –

15

Có, Perl đang phát triển một chuỗi sẽ dẫn đến lặp lại reallocations. Perl phân bổ thêm một chút không gian cho các chuỗi, nhưng chỉ một vài byte. Bạn có thể thấy điều này bằng cách sử dụng Devel :: Peek. Sự phân bổ lại này rất nhanh và thường không thực sự sao chép bộ nhớ. Tin tưởng người quản lý bộ nhớ của bạn, đó là lý do tại sao bạn đang lập trình trong Perl và không C. Benchmark nó đầu tiên!

Bạn có thể preallocate mảng với $#array = $num_entries và một băm với keys %hash = $num_keys nhưng length $string = $strlen không hoạt động. Đây là số clever trick I dug up on Perlmonks.

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

Hoặc nếu bạn muốn tham gia XS, bạn có thể gọi SvGROW().

đề xuất hỗn loạn 'để sử dụng một mảng và sau đó kết hợp tất cả lại với nhau sẽ sử dụng nhiều hơn gấp đôi bộ nhớ. Bộ nhớ cho mảng. Bộ nhớ cho mỗi vô hướng được phân bổ cho từng phần tử trong mảng. Bộ nhớ cho chuỗi được giữ trong mỗi phần tử vô hướng. Bộ nhớ cho bản sao khi tham gia. Nếu nó kết quả trong mã đơn giản hơn, hãy làm điều đó, nhưng đừng nghĩ rằng bạn đang lưu bất kỳ bộ nhớ nào.

0

tôi sẽ đi mảng/join cách:

push(@array, $crunched_bit) 

Và sau đó $str = join('', @array), nếu không có gì hơn, để có quyền truy cập vào tất cả các yếu tố để gỡ lỗi tại một số thời gian sau đó.

+0

Điều này sẽ sử dụng khá nhiều bộ nhớ bổ sung vì mọi phần tử mảng cần có SV mới. –

3

Tôi không biết cụ thể cách chuỗi Perl được triển khai nhưng dự đoán khá tốt là đó là constant amortized time. Điều này có nghĩa rằng ngay cả khi bạn tìm cách để phân bổ trước cơ hội chuỗi của bạn là thời gian kết hợp nó sẽ tiết kiệm cho tất cả người dùng của tập lệnh sẽ nhỏ hơn thời gian bạn đã yêu cầu this question trên Stack Overflow.

Các vấn đề liên quan