2006年12月07日 16:30 [Edit]

perl - DateTime->now(time_zone => $obj) # better practice

camel

私はあまりDateTimeは使ってないのだけど、better practiceを見つけたと思うので。

[perl] DateTime->now(time_zone => 'local')って激遅 - おいぬま日報(不定期) (2006-12-06)
Perl界隈の日付操作モジュールのデファクトスタンダードであるDateTimeですが、今まで「タイムゾーンなんてシステムで定義されているものから勝手に取ってきてほしい」という理由で

DateTime->now(time_zone => 'local');

なんてことをしてたのですが、これが激遅なことが発覚しました。


二行で書くと、以下のとおり。

my $tzhere = DateTime::TimeZone->new( name => 'local' );
my $dt = DateTime->now(time_zone => $tzhere);

要は、あらかじめDateTime::TimeZoneオブジェクトをこさえといて、それを使い回せということ。これだと明示指定以上に高速になるし、ポータビリティを損なうこともありません。

dtbench.pl
#!/usr/bin/perl
use strict;
use DateTime;
use DateTime::TimeZone;
use Benchmark qw/cmpthese timethese/;

sub gentest {
    my $tz = shift;
    sub { DateTime->now( time_zone => $tz ) }
}
my $tzhere = DateTime::TimeZone->new( name => 'local' );
my %these;
for my $tz ( 'local', 'Asia/Tokyo', $tzhere ) {
    my $name = ref($tz) ? q($tz) : qq('$tz');
    $these{ "now($name)" } = gentest($tz);
}
cmpthese( timethese( 0, \%these ) );
FreeBSD 6-STABLE
  now($tz):  3 wallclock secs ( 3.16 usr +  0.02 sys =  3.18 CPU) @ 2279.15/s (n=7247)
now('Asia/Tokyo'):  4 wallclock secs ( 3.19 usr +  0.02 sys =  3.20 CPU) @ 2029.58/s (n=6501)
now('local'):  4 wallclock secs ( 0.87 usr +  2.36 sys =  3.23 CPU) @ 67.25/s (n=217)
                    Rate      now('local') now('Asia/Tokyo')          now($tz)
now('local')      67.3/s                --              -97%              -97%
now('Asia/Tokyo') 2030/s             2918%                --              -11%
now($tz)          2279/s             3289%               12%                --
Mac OS X v10.4.8
 now($tz):  4 wallclock secs ( 3.23 usr +  0.02 sys =  3.25 CPU) @ 2871.08/s (n=9331)
now('Asia/Tokyo'):  3 wallclock secs ( 3.18 usr +  0.01 sys =  3.19 CPU) @ 2616.61/s (n=8347)
now('local'):  3 wallclock secs ( 3.09 usr +  0.16 sys =  3.25 CPU) @ 1767.38/s (n=5744)
                    Rate      now('local') now('Asia/Tokyo')          now($tz)
now('local')      1767/s                --              -32%              -38%
now('Asia/Tokyo') 2617/s               48%                --               -9%
now($tz)          2871/s               62%               10%                --

ちなみに、OS Xの方が差が少ないのは、/etc/localtimeがsymlinkだから。DateTime::TimeZoneはこの場合はSymlinkを読んでタイムゾーン名をすぐに割り出すのだけど、実ファイルの場合は、/usr/share/zoneinfo以下のファイルを全部File::Compareで比較するという涙ぐましい方法で調べます。

こんなところかな。

Dan the Man with Too Many Timezones to Cover


この記事へのトラックバックURL

この記事へのコメント
6-STABLE やめて 7-STABLE にしませんかー

# 常用マシンでないので CURRENT で生きてます
Posted by a BSDer at 2009年06月28日 01:02
こうですかねぇ。
DateTime::TimeZone->new() で全 TimeZone name に大してキャッシュを
持つのもアリな気がしますが、そうしないのは何か理由でもあるんでしょうか…。

xeres@notepc:~/datetime$ diff $(perldoc -ml DateTime::TimeZone::Local) Local.pm -dur
--- /usr/share/perl5/DateTime/TimeZone/Local.pm 2006-04-04 05:58:40.000000000 +0900
+++ Local.pm 2006-12-07 22:10:23.295367757 +0900
@@ -4,8 +4,10 @@

use File::Spec;

+my $LocalTZCache;
sub local_time_zone
{
+ return $LocalTZCache if $LocalTZCache;
my $tz;

foreach ( qw( env
@@ -19,7 +21,7 @@
my $meth = "_from_$_";
$tz = __PACKAGE__->$meth();

- return $tz if $tz;
+ return $LocalTZCache = $tz if $tz;
}

die "Cannot determine local time zone\n";
Posted by xeres at 2006年12月07日 22:13