読者です 読者をやめる 読者になる 読者になる

ハッシュ生成時のメモリコピー

perl

ハッシュが余りに便利すぎて半笑いなのだけど、性能的にメモリコピーが気になった。

use strict;
use warnings;

my $str = 'string_hogehoge';
my $hash = {
    data => 'hash_hogehoge',
};

sub hash_from_array {
    my $arg = {@_};

    $arg->{data} = 'changed_in___hash_from_array';
    print $arg->{data}."\n";
}

sub hash_ref {
    my $arg = shift;

    $arg->{data} = 'changed_in___hash_ref';
    print $arg->{data}."\n";
}

print "====== from array #1=====\n";
hash_from_array(
    data => $str
);
print $str."\n";

print "====== from array #2=====\n";
hash_from_array(
    data => $hash->{data},
);
print $hash->{data}."\n";


print "======= reference #1 ====\n";
hash_ref(
    {
        data => $str,
    }
);
print $str."\n";

print "======= reference #2 ====\n";
hash_ref( $hash);
print $hash->{data}."\n";

ハッシュや文字列から無名ハッシュを生成したり、ハッシュのリファレンス渡しをしてみたところ、

$ perl hash.pl
====== from array #1=====
changed_in___hash_from_array
string_hogehoge
====== from array #2=====
changed_in___hash_from_array
hash_hogehoge
======= reference #1 ====
changed_in___hash_ref
string_hogehoge
======= reference #2 ====
changed_in___hash_ref
changed_in___hash_ref

ハッシュのリファレンス渡しをする以外のパターンは、全てコピーが発生しているような気がする。アドレスが見れれば嬉しいのだけど。でもcopy on writeの仕組みがあったりすると上記テストだとわからないんだよなぁ。気になる…

以下のベンチマーク結果からは影響が無いようにも思える…

use strict;
use warnings;
use Benchmark qw/cmpthese timethese/;

my $buf = "A" x (1024*1024);

cmpthese(
    timethese(
        0,
        {
            normal =>              sub { hoge_normal             ( { data => $buf, }); },
            normal_noaccess =>     sub { hoge_normal_noaccess    ( { data => $buf, }); },
            normal_nop =>          sub { hoge_normal             ( { data => $buf, }); },
            from_array =>          sub { hoge_from_array         ( data => $buf,); },
            from_array_noaccess => sub { hoge_from_array_noaccess( data => $buf,); },
            from_array_nop =>      sub { hoge_from_array         ( data => $buf,); },
        }
    )
);

sub hoge_normal {
    my $hash = shift;
    $hash->{data} .= "Z";
}

sub hoge_normal_noaccess { my $hash = shift; }

sub hoge_normal_nop {}

sub hoge_from_array {
    my $hash = {@_};
    $hash->{data} .= "Z";
}

sub hoge_from_array_noaccess { my $hash = {@_}; }

sub hoge_from_array_nop {}

全部一緒だ… copy on writeも無さそうだ。全体の負荷に対してメモリコピーの負荷が極小ってことかなぁ…

Benchmark: running from_array, from_array_noaccess, from_array_nop, normal, normal_noaccess, normal_nop for at least 3 CPU seconds...
from_array:  3 wallclock secs ( 1.53 usr +  1.47 sys =  3.00 CPU) @ 152.67/s (n=458)
from_array_noaccess:  3 wallclock secs ( 1.75 usr +  1.47 sys =  3.22 CPU) @ 152.80/s (n=492)
from_array_nop:  3 wallclock secs ( 1.68 usr +  1.46 sys =  3.14 CPU) @ 151.91/s (n=477)
    normal:  3 wallclock secs ( 1.64 usr +  1.50 sys =  3.14 CPU) @ 151.91/s (n=477)
normal_noaccess:  3 wallclock secs ( 1.67 usr +  1.57 sys =  3.24 CPU) @ 152.16/s (n=493)
normal_nop:  3 wallclock secs ( 1.66 usr +  1.48 sys =  3.14 CPU) @ 151.91/s (n=477)
                     Rate normal_nop normal from_array_nop normal_noaccess from_array from_array_noaccess
normal_nop          152/s         --    -0%            -0%             -0%        -0%                 -1%
normal              152/s         0%     --             0%             -0%        -0%                 -1%
from_array_nop      152/s         0%     0%             --             -0%        -0%                 -1%
normal_noaccess     152/s         0%     0%             0%              --        -0%                 -0%
from_array          153/s         0%     0%             0%              0%         --                 -0%
from_array_noaccess 153/s         1%     1%             1%              0%         0%                  --