リファレンス

値、特に大きな配列など、たくさんのデータを渡すときには、「リファレンス」というものを用いると良いらしい。・・・ということで、どのくらい有効なのか?と思ってやってみた。

一応書いておきますが、サンプルで実行できるのは、下の方に書いてある「リファレンスの使用法」みたいなものです。ベンチマークとは関係ありませんので、適当に実行してください。

まず、基準となる配列「$cnt個」に乱数「rand」を格納した。それを「my」変数に書き込むサブルーチンを作って「$count」回「Benchmark」してみました。(基本的な書式)

$countの値 $cntの値 リファレンス不使用 リファレンス使用
Type1 1000 100 7.14 2.48
Type2 1000 10 1.87 1.26
Type3 10000 1 12.97 11.58
Type4 100 1000 5.77 1.32

この計測が正しい方法なら、配列が大きければ大きいほど、「リファレンス」を使ったほうが処理が速いようだ。

ちなみに、「print」に関係する部分を削除すると、より明らかに差が出た。

ただ、「リファレンス」を参照する(本には「デリファレンス」と書いてあった)には、配列なら「@」を、ハッシュなら「%」を「リファレンス変数」の前につける必要がある。実は、これが結構面倒だったりする。

また、個別に参照するには通常と同じように「$」を頭につける。つまり「$$array[0]」や「$$hash{num}」のように・・・。で、なぜか知らないけど、配列の場合だけは「@$array[0]」でも参照できる。普通の配列でも「@a[0]」で参照できる(この辺はおそらくPerl5のみ)。このおかげで「@」が「"(ダブルクォート)」の中では展開されるようになり、文字として表示するには「エスケープ」しないといけなくなったんだろう。

この他に「矢印記法」というものもあり、例えば「$$array[0](または@$array[0])」の場合は「$array->[0]」と記述する事もできる。ハッシュなら「$$hash{num}」が「$hash->{num}」といった具合。ややこしいけど、なかなか面白いことを考える。

この「リファレンス」というやつは、サブルーチンに対しても作る事ができるし、さらに、その「リファレンスされたサブルーチン」が、それぞれ違う値を返したり・・・と、なかなか奥が深い。理解するのは結構時間がかかりそうだ。

ソースコード

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#!/usr/bin/perl

#BEGIN{
#   print "Content-type: text/plainnn";
#   open(STDERR, ">&STDOUT");
#   $|=1;
#}

%h = (
    'num1'    => 1,
    'num2'    => 2,
);

@a = (1,2);

$hash = %h;
$array = @a;

print <<EOM;
Content-type: text/plain

$h{num1} = $h{num1}

$$hash{num1} = $$hash{num1}
%$hash{num1} = %$hash{num1}
$hash->{num1} = $hash->{num1}

$a[0] = $a[0]
@a[0] = @a[0]

$$array[0] = $$array[0]
@$array[0]  = @$array[0]
$array->[0] = $array->[0]

EOM

ベンチマーク用ソースコード

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#!/usr/bin/perl

BEGIN{
    print "Content-type: text/plainnn";
    open(STDERR, ">&STDOUT");
    $|=1;
}

use Benchmark;

$count  = 1000;
$cnt    = 100;
$dat1   = "reftest1.txt";
$dat2   = "reftest2.txt";

srand;
for(0 .. $cnt){
    @nums[$_] = rand;
}

open(OUT, "> $dat1");
close(OUT);
open(OUT, "> $dat2");
close(OUT);

sleep 1;

@t = timethese($count, {
    'Refuse'  =>   '&use_ref;',
    'RefNouse'    =>   '&nouse_ref;',
});

exit(0);

sub use_ref{
    my $r_nums;
    $r_nums = @nums;
    open(OUT, ">> $dat1");
    foreach(@$r_nums){
        print OUT "$_\n";
    }
    close(OUT);
}

sub nouse_ref{
    my @n_nums;
    @n_nums = @nums;
    open(OUT, ">> $dat2");
    foreach(@n_nums){
        print OUT "$_\n";
    }
    close(OUT);
}
comments powered by Disqus
Hugo で構築されています。
テーマ StackJimmy によって設計されています。