develooper Front page | perl.perl5.porters | Postings from April 2007

Re: Performance problems with Hash::Util::FieldHash

Thread Previous | Thread Next
From:
Abigail
Date:
April 18, 2007 09:35
Subject:
Re: Performance problems with Hash::Util::FieldHash
Message ID:
20070418163432.GA9598@abigail.nl
On Wed, Apr 18, 2007 at 10:31:25AM -0400, Jerry D. Hedden wrote:
> I began looking at adapting Object::InsideOut to using
> Hash::Util::FieldHash.  My first endeavor was to see how
> fast they were.  Much to my surprise, their performance is
> disappointing.  I compared simple get and set accessors for
> inside-out classes using field hashes, refaddr and
> stringified objects.  (See attachments.  The code is quite
> simple.)
> 
>    'get' operations:
>                  Rate   Refaddr FieldHash Stringify
>    Refaddr   151543/s        --      -25%      -49%
>    FieldHash 201418/s       33%        --      -32%
>    Stringify 297227/s       96%       48%        --
> 
>    'set' operations:
>                  Rate FieldHash   Refaddr Stringify
>    FieldHash  79496/s        --      -49%      -74%
>    Refaddr   156784/s       97%        --      -49%
>    Stringify 309688/s      290%       98%        --
> 
> While field hashes do a bit better than refaddr in get
> operations, stringification is the hands down winner!  Why?
> 
> I tried to speed things up a bit by borrowing the trick from
> blead 28961, and replaced HUF_id with
> 
>    SV* HUF_id(SV* ref, NV cookie) {
>        UV id = PTR2UV(SvRV(ref));
>        return sv_2mortal(newSVpvn((char *)&id, sizeof(id)));
>    }
> 
> This only have about a 10% improvement:
> 
>    'get' operations:
>                  Rate   Refaddr FieldHash Stringify
>    Refaddr   151543/s        --      -31%      -49%
>    FieldHash 221005/s       46%        --      -25%
>    Stringify 294645/s       94%       33%        --
> 
>    'set' operations:
>                  Rate FieldHash   Refaddr Stringify
>    FieldHash  85843/s        --      -45%      -73%
>    Refaddr   155647/s       81%        --      -50%
>    Stringify 312161/s      264%      101%        --
> 
> And, of course, all this pales in comparison to what
> Object::InsideOut already does.
> 
>    'get' operations:
>                  Rate   Refaddr FieldHash Stringify       OIO
>    Refaddr   150377/s        --      -25%      -50%      -83%
>    FieldHash 199990/s       33%        --      -33%      -77%
>    Stringify 300178/s      100%       50%        --      -66%
>    OIO       870557/s      479%      335%      190%        --
> 
>    'set' operations:
>                  Rate FieldHash   Refaddr Stringify       OIO
>    FieldHash  79518/s        --      -49%      -75%      -91%
>    Refaddr   154844/s       95%        --      -51%      -82%
>    Stringify 316440/s      298%      104%        --      -64%
>    OIO       867219/s      991%      460%      174%        --
> 
> While I fully appreciate the significance of the work done
> by Anno, unless something drastic can be done to improve
> their performance, there doesn't seem to be any reason to
> bother trying to use field hashes.


Well, that sounds like a very harsh conclusion. Unless one's attitude is
that speed if everything - in which case you haven't any reason to bother
using Perl in the first place.

*I* certainly will be using fieldhashes. Even if it's slower.

Having said that, I did some testing as well. I don't get much difference
between 'refaddr' and stringification. Fieldhashes are slower when
setting/getting, although the difference is much less on getting.


Create/DESTROY
        refaddr:   14.45 usec
        stringify: 13.03 usec
        fieldhash:  5.62 usec
Set
        refaddr:    5.86 usec
        stringify:  5.01 usec
        fieldhash:  7.70 usec
Get
        refaddr:    3.18 usec
        stringify:  2.78 usec
        fieldhash:  3.60 usec
Cycle
        refaddr:   24.27 usec
        stringify: 21.47 usec
        fieldhash: 35.63 usec



#!/opt/perl/current/bin/perl

use strict;
use warnings;
no warnings 'syntax';

use Time::HiRes 'time';

my $RUNS = 1_000_000;

{
    package Refaddr;
    use Scalar::Util 'refaddr';

    my %attr;

    sub new {bless \do {my $var} => shift}

    sub set {
        my $key = refaddr (my $self = shift);
        $attr {$key} = shift;
    }

    sub get {
        my $key = refaddr (my $self = shift);
        $attr {$key};
    }

    sub DESTROY {
        my $key = refaddr (my $self = shift);
        delete $attr {$key};
    }
}

{
    package Stringify;

    my %attr;

    sub new {bless \do {my $var} => shift}

    sub set {
        my $self = shift;
        $attr {$self} = shift;
    }

    sub get {
        my $self = shift;
        $attr {$self};
    }

    sub DESTROY {
        my $self = shift;
        delete $attr {$self};
    }
}

{
    package FieldHash;
    use Hash::Util::FieldHash 'fieldhash';

    fieldhash my %attr;

    sub new {bless \do {my $var} => shift}

    sub set {
        my $self = shift;
        $attr {$self} = shift;
    }

    sub get {
        my $self = shift;
        $attr {$self};
    }
}

my $t0 = time;
for (1 .. $RUNS) {my $obj = Refaddr   -> new;}
my $t1 = time;
for (1 .. $RUNS) {my $obj = Stringify -> new;}
my $t2 = time;
for (1 .. $RUNS) {my $obj = FieldHash -> new;}
my $t3 = time;

my $c_r = $t1 - $t0;
my $c_s = $t2 - $t1;
my $c_f = $t3 - $t2;

my $obj_r = Refaddr   -> new;
my $obj_s = Stringify -> new;
my $obj_f = FieldHash -> new;

   $t0 = time;
for (1 .. $RUNS) {$obj_r -> set (1)}
   $t1 = time;
for (1 .. $RUNS) {$obj_s -> set (1)}
   $t2 = time;
for (1 .. $RUNS) {$obj_f -> set (1)}
   $t3 = time;

my $s_r = $t1 - $t0;
my $s_s = $t2 - $t1;
my $s_f = $t3 - $t2;


   $t0 = time;
for (1 .. $RUNS) {$obj_r -> get}
   $t1 = time;
for (1 .. $RUNS) {$obj_s -> get}
   $t2 = time;
for (1 .. $RUNS) {$obj_f -> get}
   $t3 = time;

my $g_r = $t1 - $t0;
my $g_s = $t2 - $t1;
my $g_f = $t3 - $t2;


   $t0 = time;
for (1 .. $RUNS) {my $o = Refaddr   -> new; $o -> set (1); $o -> get;}
   $t1 = time;
for (1 .. $RUNS) {my $o = Stringify -> new; $o -> set (1); $o -> get;}
   $t2 = time;
for (1 .. $RUNS) {my $o = FieldHash -> new; $o -> set (1); $o -> get;}
   $t3 = time;

my $z_r = $t1 - $t0;
my $z_s = $t2 - $t1;
my $z_f = $t3 - $t2;


printf "Create/DESTROY\n";
printf "\trefaddr:   %5.2f usec\n" => 1_000_000 * $c_r / $RUNS;
printf "\tstringify: %5.2f usec\n" => 1_000_000 * $c_s / $RUNS;
printf "\tfieldhash: %5.2f usec\n" => 1_000_000 * $c_f / $RUNS;

printf "Set\n";
printf "\trefaddr:   %5.2f usec\n" => 1_000_000 * $s_r / $RUNS;
printf "\tstringify: %5.2f usec\n" => 1_000_000 * $s_s / $RUNS;
printf "\tfieldhash: %5.2f usec\n" => 1_000_000 * $s_f / $RUNS;

printf "Get\n";
printf "\trefaddr:   %5.2f usec\n" => 1_000_000 * $g_r / $RUNS;
printf "\tstringify: %5.2f usec\n" => 1_000_000 * $g_s / $RUNS;
printf "\tfieldhash: %5.2f usec\n" => 1_000_000 * $g_f / $RUNS;

printf "Cycle\n";
printf "\trefaddr:   %5.2f usec\n" => 1_000_000 * $z_r / $RUNS;
printf "\tstringify: %5.2f usec\n" => 1_000_000 * $z_s / $RUNS;
printf "\tfieldhash: %5.2f usec\n" => 1_000_000 * $z_f / $RUNS;

__END__

Thread Previous | Thread Next


nntp.perl.org: Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at ask@perl.org | Group listing | About