2009-06-15 27 views
8

Tôi đã làm việc trên một số tập lệnh Perl xử lý các tệp dữ liệu có chiều rộng cố định lớn, trích xuất các bản chất nhỏ ra khỏi mỗi bản ghi dữ liệu. Tôi đã tưởng tượng rằng việc ủy ​​thác việc giải nén các chất nền cho các cuộc gọi phương thức sẽ tốn kém vì chi phí sao chép bản ghi dữ liệu vào mảng @_. Vì vậy, tôi chạy sau đây để so sánh (a) gọi trực tiếp đến substr(), (b) gọi phương thức truyền dữ liệu dưới dạng chuỗi, và (c) gọi phương thức truyền dữ liệu theo tham chiếu.Bạn có phải chịu ảnh hưởng của việc sao chép dữ liệu khi truyền đối số tới các chương trình con Perl không?

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

my $RECORD = '0' x 50000; 

my $direct = sub { my $v = substr($RECORD, $_, 1) for 0..999 }; 
my $byVal = sub { my $v = ByVal ($RECORD, $_) for 0..999 }; 
my $byRef = sub { my $v = ByRef (\$RECORD, $_) for 0..999 }; 

sub ByVal { return substr( $_[0], $_[1], 1) } 
sub ByRef { return substr(${$_[0]}, $_[1], 1) } 

timethese(10000, { 
    direct => $direct, 
    byVal  => $byVal, 
    byRef  => $byRef, 
}); 

my $byVal2loc = sub { my $v = ByVal2loc($RECORD, $_) for 0..999 }; 
my $byRef2loc = sub { my $v = ByRef2loc(\$RECORD, $_) for 0..999 }; 

sub ByVal2loc { my $arg = shift; return substr( $arg, $_[0], 1) } 
sub ByRef2loc { my $arg = shift; return substr($$arg, $_[0], 1) } 

timethese($ARGV[0], { 
    byVal2loc => $byVal2loc, 
    byRef2loc => $byRef2loc, 
}); 

# Produces this output: 
Benchmark: timing 10000 iterations of byRef, byVal, direct... 
    byRef: 19 wallclock secs... 
    byVal: 15 wallclock secs... 
    direct: 4 wallclock secs... 

Benchmark: timing 10000 iterations of byRef2loc, byVal2loc... 
byRef2loc: 21 wallclock secs... 
byVal2loc: 119 wallclock secs... 

Như dự kiến, phương pháp trực tiếp là nhanh nhất. Tuy nhiên, tôi đã ngạc nhiên khi thấy không có hình phạt liên quan đến việc "sao chép dữ liệu" mà tôi đã tưởng tượng. Ngay cả khi tôi tăng chiều rộng của bản ghi lên tỷ lệ outlandish (ví dụ, một tỷ ký tự), các giá trị theo giá trị và tham chiếu về cơ bản giống nhau.

Dường như khi chuyển đối số cho phương thức, Perl không sao chép dữ liệu. Tôi đoán điều này có ý nghĩa khi phản ánh thêm về sức mạnh bí danh của @_. Các đối số được truyền theo tham chiếu, không phải theo giá trị.

Tuy nhiên, đây là hình thức giới hạn tham chiếu không giới hạn vì không thể gán trực tiếp tham chiếu trong @_ cho biến cục bộ trong chương trình con. Các bài tập như vậy sẽ dẫn đến việc sao chép dữ liệu, như được minh họa bằng tập hợp điểm chuẩn thứ hai.

Tôi có hiểu chính xác điều này không?

Trả lời

8

Vâng, nhiệm vụ sao chép; chỉ cần vượt qua các đối số thì không. Tuy nhiên, bạn có thể đặt biệt hiệu cho các phần tử trong @_ bằng cách sử dụng Lexical::Alias. chuẩn sửa đổi này cho thấy làm điều đó một phần ba nhanh như sử dụng một tài liệu tham khảo, nhưng liên tục nên không phụ thuộc vào chiều dài của $ RECORD:

use strict; 
use warnings; 
use Benchmark qw(timethese); 
use Lexical::Alias; 

my $RECORD = '0' x 5000000; 

my $byVal2loc = sub { my $v = ByVal2loc($RECORD, $_) for 0..999 }; 
my $byRef2loc = sub { my $v = ByRef2loc(\$RECORD, $_) for 0..999 }; 
my $byAlias2loc = sub { my $v = ByAlias2loc($RECORD, $_) for 0..999 }; 

sub ByVal2loc { my $arg = shift; return substr( $arg, $_[0], 1) } 
sub ByRef2loc { my $arg = shift; return substr($$arg, $_[0], 1) } 
sub ByAlias2loc { my $arg; alias($_[0], $arg); return substr($arg, $_[0], 1 ) } 

timethese($ARGV[0], { 
    byVal2loc => $byVal2loc, 
    byRef2loc => $byRef2loc, 
    byAlias2loc => $byAlias2loc, 
}); 

# output: 
Benchmark: running byAlias2loc, byRef2loc, byVal2loc for at least 3 CPU seconds... 
byAlias2loc: 3 wallclock secs (3.16 usr + 0.00 sys = 3.16 CPU) @ 430.70/s (n=1361) 
byRef2loc: 4 wallclock secs (3.24 usr + 0.00 sys = 3.24 CPU) @ 1329.63/s (n=4308) 
byVal2loc: 5 wallclock secs (4.95 usr + 0.01 sys = 4.96 CPU) @ 0.40/s (n=2) 
      (warning: too few iterations for a reliable count) 

(. Trực tiếp sử dụng alias_r thay vì chức năng alias helper là nhẹ nhanh hơn)

6

IIRC, trong một phụ của Perl, mảng @_ đã là tập hợp các bí danh (tham chiếu) cho các biến. Nếu bạn sửa đổi $_[0], bạn sẽ ảnh hưởng đến biến trong chức năng gọi.

#!/bin/perl -w 
use strict; 

sub x 
{ 
    print "x = $_[0]\n"; 
    $_[0] = "pinkerton"; 
    print "x = $_[0]\n"; 
} 

my $y = "abc"; 

print "y = $y\n"; 
x($y); 
print "y = $y\n"; 

Đầu ra là:

y = abc 
x = abc 
x = pinkerton 
y = pinkerton 
+0

+1. Đây là câu trả lời chính xác. –

+0

@Igor Krivokon: Đúng, có, nhưng đã được nêu trong câu hỏi, ít nhất là ngầm. Tôi đoán "Có, bạn hiểu chính xác điều này."thiếu một cái gì đó như một câu trả lời. – ysth

0

Nếu bạn muốn cung cấp cho các yếu tố của @_ tên có ý nghĩa, bạn có thể làm bí danh cho họ sử dụng Data::Alias, vì vậy

use Data::Alias; 

sub foo { 
    alias my ($a, $b, $c) = @_; 
} 

bạn có thể làm điều tương tự răng cưa vào mảng và băm.

alias my ($a, $b, @c) = @_; 
    alias my ($a, $b, %c) = @_; 

Trong thực tế, răng cưa vào một hash

alias my (%p) = @_; 

là đặc biệt mạnh mẽ vì nó cung cấp pass-by-reference tên tham số. Tốt đẹp.

(Dữ liệu :: Bí danh cung cấp phần lớn chức năng của Lexical :: Bí danh; mục đích chung hơn và mạnh hơn.)

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