2009-09-07 11 views
7

以下のサンプルモジュールでは、匿名サブルーチンをシンボルテーブルに追加することによって、ゲッターとセッターが生成されます。メソッドがこのように作成された後、結果として得られるコードは手作業で作成されたgetterおよびsetterを持つモジュールと機能的に同等(動作、速度などの面で同等)であるか、このアプローチには何らかの固有の責任がありますか? (私はいくつかの基本的なスピードのベンチマークを行っているし、これまでの任意の違いを検出していない。)Perlでは、ゲッターやセッターをハードコーディングするのではなく、不利な点がありますか?

package Module;  
use strict; 
use warnings; 

BEGIN { 
    my @attr = qw(author title number); 
    no strict 'refs'; 
    for my $a (@attr){ 
     *{__PACKAGE__ . "::get_$a"} = sub { $_[0]->{$a}   }; 
     *{__PACKAGE__ . "::set_$a"} = sub { $_[0]->{$a} = $_[1] }; 
    } 
} 

sub new { 
    my $class = shift; 
    bless { @_ }, $class; 
} 

1; 
+0

'* {" get_ $ a "} = sub' ...も動作するはずです。 (そこに '__PACKAGE__'は必要ありません) –

答えて

8

に比べ

BEGIN { 
    no strict 'refs'; 
    for my $a ("aaaa".."zzzz"){ 
     *{__PACKAGE__ . "::get_$a"} = sub { $_[0]->{$a}   }; 
     *{__PACKAGE__ . "::set_$a"} = sub { $_[0]->{$a} = $_[1] }; 
    } 
} 
print `ps -F -p $$`; # adjust for your ps syntax 

: 、例えば、これを試してみてください。ただし、文字列evalを使用してサブルーチンを作成しない限り、これは通常不可能です。例えば、コードは、あなたが提供:

... = sub { $_[0]->{$a} }; 

は手動で書かれていたコードよりも、これまで、それほどわずかに遅くなります。前者はの値を取得する必要があり

sub foo { $_[0]->{'foo'} } 

というだけの理由変数$ aをハッシュのキーとして使用する前に、後ではそのハッシュキーとして定数を使用します。また、脇の下として、shiftは、通常、$_[0]よりも速くなる傾向があります。ここではいくつかのベンチマークコードは次のとおりです。

use Benchmark qw(cmpthese); 

package Foo; 

sub manual_shift { shift->{'foo'} } 
sub manual_index { $_[0]->{'foo'} } 

my $attr = 'foo'; 

*dynamic_shift = sub { shift->{$attr} }; 
*dynamic_index = sub { $_[0]->{$attr} }; 

package main; 

my $o = bless { foo => 123 }, 'Foo'; 

cmpthese(-2, { 
    manual_shift => sub { my $a = $o->manual_shift }, 
    manual_index => sub { my $a = $o->manual_index }, 
    dynamic_shift => sub { my $a = $o->dynamic_shift }, 
    dynamic_index => sub { my $a = $o->dynamic_index }, 
}); 

と私のシステム上の結果:

    Rate dynamic_index manual_index dynamic_shift manual_shift 
dynamic_index 1799024/s   --   -3%   -4%   -7% 
manual_index 1853616/s   3%   --   -1%   -4% 
dynamic_shift 1873183/s   4%   1%   --   -3% 
manual_shift 1937019/s   8%   4%   3%   -- 

彼らは違いがノイズで迷子ことがとても近くだが、多くの臨床試験の上に、私はあなたが見ると思います「手動シフト」の変形が最も速いことを意味する。しかし、このようなすべてのマイクロベンチマークと同様に、あなたのハードウェアとperlのバージョンを正確にテストして、何かを確認する必要があります。

ここには、ストリングevalがミックスされています。

eval "sub eval_index { \$_[0]->{'$attr'} }"; 
eval "sub eval_shift { shift->{'$attr'} }"; 

統計的ノイズをプラスまたはマイナスした「手動」バリアントとまったく同じである必要があります。私の結果は:

    Rate dynamic_index manual_index dynamic_shift manual_shift eval_shift eval_index 
dynamic_index 1820444/s   --   -1%   -2%   -3%  -4%  -5% 
manual_index 1835005/s   1%   --   -1%   -2%  -3%  -4% 
dynamic_shift 1858131/s   2%   1%   --   -1%  -2%  -3% 
manual_shift 1876708/s   3%   2%   1%   --  -1%  -2% 
eval_shift 1894132/s   4%   3%   2%   1%   --  -1% 
eval_index 1914060/s   5%   4%   3%   2%   1%   -- 

繰り返しますが、これらはすべて、あなたがせっかくを取るとノイズから信号を整理するために多くの臨床試験を行う必要があるだろうことをとても近くにあります。しかし、ハッシュ・キーとして定数を使用することと、ハッシュ・キーとして変数(その値を最初に検索する必要がある)を使用することの違いは、一貫して示す必要があります。 (shiftの最適化は別の問題であり、perlの過去または将来のバージョンでどちらか一方を変更する可能性が高くなります)。

+0

あなたのeval_indexは$ in $ _ [0]がエスケープされている必要があります。 – ysth

+1

アクセッサを生成する場合は、FASTジェネレータを使用することもできます。 Cf。 http://search.cpan.org/dist/Class-XSAccessorこれは、直接ハッシュアクセス以外の何かより高速です。 – tsee

+0

ysth:ありがとう、私はローカルで編集中にバックスラッシュが食べられたと思います。私はそれを修正しました。 –

7

ので、違いはありません。

BEGIN { *Some_package::foo = sub { ... } } 

sub Some_package::foo { ... } 

がためだけの速記です

参照perlmod

2

どちらの方法も、コンパイル時のシンボルテーブルへのルーチン参照。ビヘイビアとランタイムのパフォーマンスはまったく同じになります。コンパイル時間の差がごくわずか(すなわち、無視できる)である可能性があります。

同様のアプローチは、実行時に小さな影響を与えるAUTOLOADによってオンデマンドでアクセサを生成することです。 AUTOLOADアプローチを使用すると、$object->can()などの動作を変更することもできます。

明らかに、メソッドを生成すると、ctagsのようなツールを含む静的解析のどのフォームからもそれらを隠すことになります。

+1

真剣に。そのようなことにAUTOLOADを使用しないでください。それは狂気に洪水の門を開きます。 (私は本当にあなた、MC、それを知っていると確信しています:) – tsee

+3

** '警告:' **あなたがしていることを**正確に**知っていない限り、 'AUTOLOAD'を使用しないでください。 –

2

実行時の動作とパフォーマンスは、ほとんど同じです(メソッドがクロージャであるかどうかを気にしない限り)。

属性の数が多いと、コンパイル時間とメモリ使用に違いがあります...手動で作成されたものではなく、生成されたgetterとsetterの両方が優先されます。実行時のパフォーマンスに差があってはならない結果のコードは、どちらの場合も同じである場合

sub get_aaaa { $_[0]->{aaaa}   } 
sub set_aaaa { $_[0]->{aaaa} = $_[1] } 
sub get_aaab { $_[0]->{aaab}   } 
... 
sub set_zzzy { $_[0]->{zzzy} = $_[1] } 
sub get_zzzz { $_[0]->{zzzz}   } 
sub set_zzzz { $_[0]->{zzzz} = $_[1] } 
print `ps -F -p $$`; # adjust for your ps syntax 
2

唯一の違いは起動時間です。単純なコード生成スキームでは、その違いを測ることは難しいでしょう。より複雑なシステムの場合は、それが加算されます。

この例の実際の例は、Mooseです。 Mooseはあらゆる種類の素晴らしいコード生成を行いますが、起動時間に大きな影響を与えます。これは、Mooseの開発者がpmcファイル内でa scheme to cache generated codeに取り組んでいて、毎回コードを再生成する代わりにそれらを読み込む問題で十分です。

Class::Structのようなものも考えてください。それは、文字列評価を使用してコード生成を行います(私が最後にチェックした時)。たとえそうであっても、それは非常に簡単であるため、始動時に大幅なスローダウンを引き起こすことはありません。

6

よく生成されるアクセサの主な欠点は、静的解析に依存するツールを無効にすることです。 IDEのメソッドの自動補完など。これが大きなプロジェクトの一部であれば、Mooseをご覧ください。アクセサーの生成は正しく行われました(そしてはるかに)。 IDEにサポートが追加されているので、前述の問題が適切な時期に消えてしまうほど人気が​​あります。

CPANには使いやすく、適度に効率的なコードを生成する多くのアクセッサジェネレータがあります。パフォーマンスに問題がある場合は、アクセサメソッドを使用することを前提としていますが、アクセッサに高度に最適化されたC/XSコードを使用しているため、Class::XSAccessorより高速に処理することはできません。

あなた自身のアクセサ生成コードをローリングすることは、すべてのオプションの中で最悪です。静的解析を永遠に破り、読みにくく、新しいバグが潜在的に発生します。

2

他にも言及した優れた点以外にも、私が見つけた主な欠点を追加したいと思います。それらはすべてプロファイラの匿名サブルーチンとして表示されます。どんな理由であれ、Devel::DProfはちょうど名前を理解する方法を知らない。

今私は希望より新しいDevel::NYTProfが良い仕事をするかもしれないが、私はそれを試していないだろう。

関連する問題