Zend_Dateクラスを効率よく使うチャレンジ
序
Zend Frameworkに含まれているZend_Dateクラスは、インスタンス生成コストがとにかく高い。
どれくらい高いかというと、以下の2つのプログラムで比較してみるとなんとなく分かる。
<?php $n = 10000; if ($argc >= 2) { $n = (int)$argv[1]; } set_include_path(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'library'); require_once('Zend/Date.php'); date_default_timezone_set('Asia/Tokyo'); $start = microtime(true); for ($i = 0; $i < $n; ++$i) { $zd = new Zend_Date('2015-08-01 03:04:05'); } $end = microtime(true); printf("%8.5lf\n", $end - $start);
<?php $n = 10000; if ($argc >= 2) { $n = (int)$argv[1]; } set_include_path(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'library'); require_once('Zend/Date.php'); date_default_timezone_set('Asia/Tokyo'); $start = microtime(true); for ($i = 0; $i < $n; ++$i) { $zd = new DateTime('2015-08-01 03:04:05'); } $end = microtime(true); printf("%8.5lf\n", $end - $start);
- test0-3.php: Zend_Dateオブジェクトをキャッシュしておいて、strtotimeとsetTimestampで対応
<?php $n = 10000; if ($argc >= 2) { $n = (int)$argv[1]; } set_include_path(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'library'); require_once('Zend/Date.php'); date_default_timezone_set('Asia/Tokyo'); $zd = new Zend_Date(); $start = microtime(true); for ($i = 0; $i < $n; ++$i) { $time = strtotime('2015-08-01 03:04:05'); $zd->setTimestamp($time); } $end = microtime(true); printf("%8.5lf\n", $end - $start);
これらを実行する。
$ for i in {1,2,5,8}{,00,000} ; do > printf "%d" $i > for t in test0-1.php test0-2.php test0-3.php ; do > php ${t} ${i} | awk '{printf(",%s", $0);}' > done > printf "\n" > done 1, 0.03093, 0.00011, 0.00004 100, 0.23281, 0.00048, 0.00074 1000, 2.32891, 0.00825, 0.01088 2, 0.01160, 0.00012, 0.00005 200, 0.48956, 0.00336, 0.00224 2000, 5.51129, 0.01458, 0.02203 5, 0.02411, 0.00018, 0.00012 500, 1.08754, 0.00409, 0.00425 5000,11.39618, 0.03983, 0.06424 8, 0.03674, 0.00031, 0.00000 800, 2.20668, 0.00417, 0.00931 8000,19.13784, 0.07557, 0.11488 $
並べ替えてグラフにしてみるとこんな感じ。
本
とりあえず、欲しいものは「日時計算をして、その結果を日時文字列にしたもの」ということで話を進める。
Zend_Dateオブジェクトをキャッシュして使いまわすとよさそうということで、上記「test0-3.php」をベースに、以下のような計測をしてみる。
<?php $n = 10000; if ($argc >= 2) { $n = (int)$argv[1]; } set_include_path(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'library'); require_once('Zend/Date.php'); date_default_timezone_set('Asia/Tokyo'); function _test(Zend_Date $zd) { return $zd->toString('YYYY-MM-dd'); } $zd = new Zend_Date('2015-08-01 03:04:05'); //var_dump(_test($zd)); $start = microtime(true); for ($i = 0; $i < $n; ++$i) { _test($zd); } $end = microtime(true); printf("%8.5lf\n", $end - $start);
<?php $n = 10000; if ($argc >= 2) { $n = (int)$argv[1]; } set_include_path(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'library'); require_once('Zend/Date.php'); date_default_timezone_set('Asia/Tokyo'); function _test(Zend_Date $zd) { $ts = $zd->getTimestamp(); return date('Y-m-d', $ts); } $zd = new Zend_Date('2015-08-01 03:04:05'); //var_dump(_test($zd)); $start = microtime(true); for ($i = 0; $i < $n; ++$i) { _test($zd); } $end = microtime(true); printf("%8.5lf\n", $end - $start);
<?php $n = 10000; if ($argc >= 2) { $n = (int)$argv[1]; } set_include_path(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'library'); require_once('Zend/Date.php'); date_default_timezone_set('Asia/Tokyo'); function _test(Zend_Date $zd) { $year = $zd->get(Zend_Date::YEAR); $month = $zd->get(Zend_Date::MONTH); $day = $zd->get(Zend_Date::DAY); return sprintf("%04d-%02d-%02d", $year, $month, $day); } $zd = new Zend_Date('2015-08-01 03:04:05'); $start = microtime(true); for ($i = 0; $i < $n; ++$i) { _test($zd); } $end = microtime(true); printf("%8.5lf\n", $end - $start);
さて、実行。
$ for i in {1,2,5,8}{,00,000,0000} ; do > printf "%d" $i > for t in test1-1.php test1-2.php test1-3.php ; do > php ${t} ${i} | awk '{printf(",%s", $0);}' > done > printf "\n" > done
結果。
1, 0.00020, 0.00002, 0.00009 100, 0.01489, 0.00010, 0.01203 1000, 0.15603, 0.00634, 0.17088 10000, 1.28451, 0.04945, 1.37297 2, 0.00009, 0.00002, 0.00063 200, 0.02691, 0.00093, 0.04130 2000, 0.30510, 0.01436, 0.27488 20000, 2.93000, 0.12693, 3.37226 5, 0.00057, 0.00008, 0.00116 500, 0.08533, 0.00256, 0.07560 5000, 0.85879, 0.03124, 0.98879 50000,11.33206, 0.39485, 7.20063 8, 0.00063, 0.00009, 0.00074 800, 0.10203, 0.00917, 0.09432 8000, 0.86644, 0.03868, 0.81726 80000,12.23691, 0.57610,14.96780
さすがに各フィールドの値を取るのはコストが高いが、「test1-2.php」のケースが使えそうなレベル。
また、コードは省略するが、「test2-1.php」「test2-2.php」「test2-3.php」は時分秒まで含めるケース(「HH:mm:ss」「H:i:s」等の追加による)。
「test1-2.php/test2-2.php」はフォーマットに関係なくいいパフォーマンスを出しているが、Zend_Dateは時分秒まで含めるとそれだけコストが高くなる。
結
Zend_Dateクラスとは適度に付き合うのがよさそう(謎)。