ふと気になったのでベンチマーク取ってみた。
ベンチマーク
#!/usr/bin/perl use strict; use utf8; use warnings; use DateTime; use Benchmark qw(:all) ; my $now_1 = DateTime->now; my $now_2 = $now_1->clone; my @times = ($now_1, $now_2); my $today = DateTime->today; cmpthese(0, { 'datetime' => sub { [grep { $_ >= $today } @times]; }, 'epoch' => sub { [grep { $_->epoch >= $today->epoch } @times]; }, }); 1;
MacoTasu% perl -Ilib datetime-vs-epoch.pl Rate datetime epoch datetime 32462/s -- -84% epoch 205263/s 532% --
epoch is faster than datetime !
なんとなく予想はできていたけど、epochのほうがやはり速かった。 性能差約5倍かな。
比較の方法
そもそもDateTimeオブジェクトとかDateTime->epochの比較とかどうなってるんだってばよ ってなったのでちょっとだけコードみた。
DateTime->epoch
epochの実装をみると
package DateTime; ... sub epoch { my $self = shift; return $self->{utc_c}{epoch} if exists $self->{utc_c}{epoch}; return $self->{utc_c}{epoch} = ( $self->{utc_rd_days} - 719163 ) * SECONDS_PER_DAY + $self->{utc_rd_secs}; }
のようなになっていて、utc_rd_*などに日にちと秒数が1970年1月1日までの経過日数を引いてそこに1日分の秒数をかけて秒に変換している。 単純に数字の変換の処理をして、その後比較してるだけなので速い。
Datetimeオブジェクトそのまま
Datetimeオブジェクト同士の比較は、闇っぽくてoperatorをoverloadしている。 ゆるふわ脳なので、なんかPerlがよしなに比較してくれてるんだろと思ったけど、頑張ってたのはDateTimeのモジュールでした。
比較しているところは
package DateTime; ... sub _compare_overload { # note: $_[1]->compare( $_[0] ) is an error when $_[1] is not a # DateTime (such as the INFINITY value) return undef unless defined $_[1]; return $_[2] ? -$_[0]->compare( $_[1] ) : $_[0]->compare( $_[1] ); } sub _compare { my ( $class, $dt1, $dt2, $consistent ) = ref $_[0] ? ( undef, @_ ) : @_; return undef unless defined $dt2; if ( !ref $dt2 && ( $dt2 == INFINITY || $dt2 == NEG_INFINITY ) ) { return $dt1->{utc_rd_days} <=> $dt2; } unless ( DateTime::Helpers::can( $dt1, 'utc_rd_values' ) && DateTime::Helpers::can( $dt2, 'utc_rd_values' ) ) { my $dt1_string = overload::StrVal($dt1); my $dt2_string = overload::StrVal($dt2); Carp::croak( 'A DateTime object can only be compared to' . " another DateTime object ($dt1_string, $dt2_string)." ); } if ( !$consistent && DateTime::Helpers::can( $dt1, 'time_zone' ) && DateTime::Helpers::can( $dt2, 'time_zone' ) ) { my $is_floating1 = $dt1->time_zone->is_floating; my $is_floating2 = $dt2->time_zone->is_floating; if ( $is_floating1 && !$is_floating2 ) { $dt1 = $dt1->clone->set_time_zone( $dt2->time_zone ); } elsif ( $is_floating2 && !$is_floating1 ) { $dt2 = $dt2->clone->set_time_zone( $dt1->time_zone ); } } my @dt1_components = $dt1->utc_rd_values; my @dt2_components = $dt2->utc_rd_values; foreach my $i ( 0 .. 2 ) { return $dt1_components[$i] <=> $dt2_components[$i] if $dt1_components[$i] != $dt2_components[$i]; } return 0; }
こんな感じの実装になっていたところまで確認したけど、不思議なことに途中からの記憶が無い。
結果
DateTimeオブジェクトを使った比較をする時は、DateTime->epochで比較したほうが速いのでいいのかもしれないですね。
参考資料
Dave Rolsky / DateTime-1.20 - search.cpan.org
追記(9/1)
epoch、キャッシュされている雰囲気を感じるけど、それを除いても速いのかな。
— Ichinose Shogo (@shogo82148) September 1, 2015
とのこと疑問点をいっちー先生があげいてたのでキャッシュっぽいところコメントアウトして回してみたところ
MacoTasu% perl -Ilib datetime-vs-epoch.pl Rate datetime epoch datetime 34357/s -- -81% epoch 178069/s 418% --
以上の結果になりました。ちょっとだけスコア落ちたけどそれでも約4倍ぐらいはでました。