2010-02-22 36 views
12

Các Perl mục Hỏi đáp How do I strip blank space from the beginning/end of a string? bang rằng việc sử dụng

s/^\s+|\s+$//g; 

là chậm hơn so với làm việc đó theo hai bước:

s/^\s+//; 
s/\s+$//; 

Tại sao tuyên bố này kết hợp đáng chú ý chậm hơn so với những cái riêng biệt (đối với bất kỳ chuỗi đầu vào) ?

+1

Xem thêm http://perldoc.perl.org/perlfaq4.html#How-do-I-strip-blank-space-from-the-beginning/end-of-a-string%3f –

Trả lời

12

Thời gian chạy regex Perl chạy nhanh hơn nhiều khi làm việc với các bản cố định 'cố định' hoặc 'neo' thay vì các bản chất 'nổi'. Một chuỗi con được cố định khi bạn có thể khóa nó vào một vị trí nhất định trong chuỗi nguồn. Cả '^' và '$' đều cho phép neo đậu. Tuy nhiên, khi bạn sử dụng luân phiên '|', trình biên dịch không nhận ra các lựa chọn là cố định, vì vậy nó sử dụng ít mã được tối ưu hóa để quét toàn bộ chuỗi. Và vào cuối của quá trình, tìm kiếm các chuỗi cố định hai lần là nhiều, nhanh hơn nhiều so với tìm kiếm một chuỗi thả nổi một lần. Trên một lưu ý liên quan, đọc regcomp.c của perl sẽ làm cho bạn bị mù.

Cập nhật: Dưới đây là một số chi tiết bổ sung. Bạn có thể chạy perl với cờ '-Dr' nếu bạn đã biên dịch nó bằng hỗ trợ gỡ lỗi và nó sẽ loại bỏ dữ liệu biên dịch regex. Đây là những gì bạn nhận được:

~# debugperl -Dr -e 's/^\s+//g' 
Compiling REx `^\s+' 
size 4 Got 36 bytes for offset annotations. 
first at 2 
synthetic stclass "ANYOF[\11\12\14\15 {unicode_all}]". 
    1: BOL(2) 
    2: PLUS(4) 
    3: SPACE(0) 
    4: END(0) 
stclass "ANYOF[\11\12\14\15 {unicode_all}]" anchored(BOL) minlen 1 

# debugperl -Dr -e 's/^\s+|\s+$//g' 
Compiling REx `^\s+|\s+$' 
size 9 Got 76 bytes for offset annotations. 

    1: BRANCH(5) 
    2: BOL(3) 
    3: PLUS(9) 
    4:  SPACE(0) 
    5: BRANCH(9) 
    6: PLUS(8) 
    7:  SPACE(0) 
    8: EOL(9) 
    9: END(0) 
minlen 1 

Lưu ý từ 'neo' trong bãi đầu tiên.

+2

Bạn không thực sự cần một bản dựng gỡ lỗi của perl. Bạn cần nó cho '-Dr' để làm việc, nhưng không có nó bạn có thể' sử dụng lại 'debug'' đó là đẹp hơn anyway. – hobbs

1

Nếu điều này thực sự là trường hợp, thì đó là vì động cơ regex có thể tối ưu hóa tốt hơn cho các regex riêng lẻ hơn cho kết hợp.

Ý bạn là gì "chậm hơn đáng kể"?

+0

@Anon: chậm hơn khoảng 5 lần trên chuỗi ký tự 25 –

+1

@Anon. Xem Câu hỏi thường gặp http://perldoc.perl.org/perlfaq4.html#How-do-I-strip-blank-space-from-the-beginning/end-of-a-string%3f –

+0

Có nghĩa là bạn có thể chậm hơn đáng kể nhận thấy sự khác biệt, và trong trường hợp này bạn có thể thấy một hình phạt tốc độ rất lớn. –

3

Vì hai phương pháp tương đương về mặt logic, không có lý do cố hữu nào để chúng khác nhau về hiệu suất đánh giá. Tuy nhiên, trong thực tế, một số động cơ sẽ không thể phát hiện các tối ưu hóa trong các regex phức tạp hơn.

Trong trường hợp này, tổng thể regex kết hợp không được sửa đổi, vì vậy nó có thể khớp tại bất kỳ điểm nào trong chuỗi, trong khi ^\s+ được neo ở đầu, vì vậy nó là tầm thường, và \s+$ được neo tại cuối cùng, và cung cấp một lớp nhân vật duy nhất cho mỗi nhân vật từ cuối ngược - một công cụ được tối ưu hóa tốt sẽ nhận ra thực tế đó và sẽ khớp ngược lại, làm cho nó nhỏ gọn như một trận đấu ^\s+ ở mặt sau của đầu vào.

9

Câu trả lời khác đã chỉ ra rằng các regex được neo đầy đủ cho phép động cơ tối ưu hóa quá trình tìm kiếm, chỉ tập trung vào đầu hoặc cuối hoặc chuỗi. Có vẻ như bạn có thể thấy hiệu quả của việc tối ưu hóa này bằng cách so sánh sự khác biệt về tốc độ của hai phương pháp tiếp cận bằng cách sử dụng các chuỗi có độ dài khác nhau. Khi chuỗi được lâu hơn, regex "nổi" (sử dụng luân phiên) bị ngày càng nhiều.

use strict; 
use warnings; 
use Benchmark qw(cmpthese); 

my $ws = " \t\t\n"; 

for my $sz (1, 10, 100, 1000){ 
    my $str = $ws . ('Z' x $sz) . $ws; 
    cmpthese(-2, { 
     "alt_$sz" => sub { $_ = $str; s/^\s+|\s+$//g }, 
     "sep_$sz" => sub { $_ = $str; s/^\s+//; s/\s+$// }, 
    }); 
} 

      Rate alt_1 sep_1 
alt_1 870578/s -- -16% 
sep_1 1032017/s 19% -- 

      Rate alt_10 sep_10 
alt_10 384391/s  -- -62% 
sep_10 1010017/s 163%  -- 

      Rate alt_100 sep_100 
alt_100 61179/s  -- -92% 
sep_100 806840/s 1219%  -- 

      Rate alt_1000 sep_1000 
alt_1000 6612/s  --  -97% 
sep_1000 261102/s 3849%  -- 
Các vấn đề liên quan