perlでの2乗計算とかB::Deparseとか

分散を計算しようと思い、perlでの2乗表記を調べていました。基本的には、

$i ** 2

で事足りるようですが、はたして

$i * $i

では、実行時間に差はあるのか。


Perlで累乗計算の速度計測〜二乗を計算するなら?〜 - ほんまの走り書き技術メモ


上記事によると掛け算の方が約2倍早いという。本当だろうか。

そこで以下の2つのソースコードを実行して比較しようとしました(上記事のソースコードを流用させていただいた)。

sample01

#!/usr/bin/perl
use strict;
use warnings;

for (my $i = 0; $i < 100000000; $i++){
    my $p = $i * $i;
}

sample02

#!/usr/bin/perl
use strict;
use warnings;

for (my $i = 0; $i < 100000000; $i++){
    my $p = $i ** 2;
}

しかし、どうやらもっとスマートな比較方法があるらしい。

いまさら聞けないPerlのお役立ちワザ(3) - いまさら聞けないPerlのお役立ちワザ:ITpro

上記事内のソースコードを参考に、以下のように修正。

#!/usr/bin/perl
use strict;
use warnings;
use Benchmark qw(:all);

my $i = 100;
my $results = timethese(50_000_000,
               {'Method 1' => '$i * $i',
                'Method 2' => '$i ** 2'});

cmpthese($results);

いろいろふっ飛ばしましたがお許し下さい。


数回試行したら以下のようになった。
なお実行環境は、VMware上にUbuntu11.04(64bit),Athlon II X4 630(2.8GHz),Mem=1GB,Perl 5.12.2.

Benchmark: timing 50000000 iterations of Method 1, Method 2...
  Method 1:  4 wallclock secs ( 3.95 usr +  0.00 sys =  3.95 CPU) @ 12658227.85/s (n=50000000)
  Method 2:  4 wallclock secs ( 3.92 usr +  0.00 sys =  3.92 CPU) @ 12755102.04/s (n=50000000)
               Rate Method 1 Method 2
Method 1 12658228/s       --      -1%
Method 2 12755102/s       1%       --


Benchmark: timing 50000000 iterations of Method 1, Method 2...
  Method 1:  4 wallclock secs ( 3.80 usr +  0.00 sys =  3.80 CPU) @ 13157894.74/s (n=50000000)
  Method 2:  4 wallclock secs ( 3.74 usr +  0.00 sys =  3.74 CPU) @ 13368983.96/s (n=50000000)
               Rate Method 1 Method 2
Method 1 13157895/s       --      -2%
Method 2 13368984/s       2%       --


Benchmark: timing 50000000 iterations of Method 1, Method 2...
  Method 1:  3 wallclock secs ( 3.85 usr +  0.00 sys =  3.85 CPU) @ 12987012.99/s (n=50000000)
  Method 2:  2 wallclock secs ( 3.78 usr +  0.00 sys =  3.78 CPU) @ 13227513.23/s (n=50000000)
               Rate Method 1 Method 2
Method 1 12987013/s       --      -2%
Method 2 13227513/s       2%       --

本当は10回ほど試行して誤差を見る必要があるんでしょうが計算はパス。どうも有意な差が出そうにないんだけども、根本的な間違いをしているのだろうか…。
そのうち考えることにします。

2乗の表記方法の違い

話は戻って、表記方法によってコンパイル時の最適化による違いがあるんじゃなかろうか…と思い、B::Deparseなる便利なものを用いて確認してみた。

使用するのは上で用いた以下のプログラム。

test.pl

#!/usr/bin/perl
use strict;
use warnings;
use Benchmark qw(:all);

my $i = 100;
my $results = timethese(50_000_000,
               {'Method 1' => '$i * $i',
                'Method 2' => '$i ** 2'});

cmpthese($results);
$ perl -MO=Deparse test.pl 

use Benchmark (':all');
use warnings;
use strict 'refs';
my $i = 100;
my $results = timethese(50000000, {'Method 1', '$i * $i', 'Method 2', '$i ** 2'});
cmpthese($results);

どうやらそういう問題でもないらしい。これに関しても根本的に知識が足りていないような気がするので、追々勉強しようと思います。

終わりに

というわけで何となくスッキリしたようなしなかったような。
ただ一番の問題は、単に2乗の計算をするプログラムを書こうと思っただけなのに、いつの間にか時間を消費していたことだった。


参考:
バイトコードの最適化状態を確認する - Perl Tech - Hatena::Group::Perl
Benchmark - ベンチマーク(性能比較)を行う / Perlモジュール徹底解説 - サンプルコードによるPerl入門
Perlの調べ物:Benchmark - 美味しいもの - Yahoo!ブログ