時計を壊せ

駆け出してからそこそこ経ったWebプログラマーの雑記

Time::Strptime進捗

頭を悩ませながらのんびり作ってます。
https://github.com/karupanerura/Time-Strptime

Time::Strptimeとは

strptimeのpure perlによる実装です。
epochとoffsetだけを返すシンプルな機能を持っています。
pure perlですが最小構成のperl codeを文字列として構築してevalする事により条件分岐の回数などを極限まで減らし高速な動作を実現しています。

最近の変更

timezoneサポートを実装しました

具体的には%Z/%zを環境依存*1でparse出来るようになりました。
また、この変更に伴い、offsetを返すようになりました。parseした文字列のtimezone/offset、あるいは現在のtimezoneからoffsetを計算し、返します。
offsetの計算にはTime::TZOffsetが利用出きるケースではTime::TZOffsetを利用し、そうでないケースではTime::Localを利用しています。

localeサポートを実装しました

具体的には%b/%aがparse出来るようになりました。
Encode::LocaleとPOSIX::strftimeを使って動的に組み立てるというカッコイイことをしています。*2

ベンチマークスクリプトを更新しました

https://github.com/karupanerura/Time-Strptime/blob/master/author/benchmark.pl
Time::Pieceのインスタンスをキャッシュしてstrptimeするケースと、キャッシュしないケースを分けました。
テストも同時に実行しており、同じ結果を返すケースでのベンチマークである事がちゃんと分かるようになっています。
GMT/UTCにおいて、Time::Pieceのインスタンスをキャッシュしないケースより45%程度高速というベンチマーク結果となっております。


tpがTime::Piece、dtがDateTime、tsがTime::Strptime(本モジュール)です。

    # Subtest: GMT(+0000)
    ok 1
    ok 2
    1..2
ok 1 - GMT(+0000)
Benchmark: timing 100000 iterations of dt, dt(cached), tp, tp(cached), ts, ts(cached)...
        dt: 47 wallclock secs (46.43 usr +  0.09 sys = 46.52 CPU) @ 2149.61/s (n=100000)
dt(cached): 27 wallclock secs (27.22 usr +  0.05 sys = 27.27 CPU) @ 3667.03/s (n=100000)
        tp:  2 wallclock secs ( 1.62 usr +  0.00 sys =  1.62 CPU) @ 61728.40/s (n=100000)
tp(cached):  1 wallclock secs ( 0.96 usr +  0.01 sys =  0.97 CPU) @ 103092.78/s (n=100000)
        ts: 34 wallclock secs (34.09 usr +  0.11 sys = 34.20 CPU) @ 2923.98/s (n=100000)
ts(cached):  1 wallclock secs ( 1.12 usr +  0.00 sys =  1.12 CPU) @ 89285.71/s (n=100000)
               Rate       dt       ts dt(cached)        tp ts(cached) tp(cached)
dt           2150/s       --     -26%       -41%      -97%       -98%       -98%
ts           2924/s      36%       --       -20%      -95%       -97%       -97%
dt(cached)   3667/s      71%      25%         --      -94%       -96%       -96%
tp          61728/s    2772%    2011%      1583%        --       -31%       -40%
ts(cached)  89286/s    4054%    2954%      2335%       45%         --       -13%
tp(cached) 103093/s    4696%    3426%      2711%       67%        15%         --
    # Subtest: UTC(+0000)
    ok 1
    ok 2
    1..2
ok 2 - UTC(+0000)
Benchmark: timing 100000 iterations of dt, dt(cached), tp, tp(cached), ts, ts(cached)...
        dt: 47 wallclock secs (46.49 usr +  0.08 sys = 46.57 CPU) @ 2147.31/s (n=100000)
dt(cached): 27 wallclock secs (26.62 usr +  0.05 sys = 26.67 CPU) @ 3749.53/s (n=100000)
        tp:  1 wallclock secs ( 1.64 usr +  0.00 sys =  1.64 CPU) @ 60975.61/s (n=100000)
tp(cached):  1 wallclock secs ( 0.81 usr +  0.00 sys =  0.81 CPU) @ 123456.79/s (n=100000)
        ts: 34 wallclock secs (33.53 usr +  0.11 sys = 33.64 CPU) @ 2972.65/s (n=100000)
ts(cached):  1 wallclock secs ( 1.13 usr +  0.00 sys =  1.13 CPU) @ 88495.58/s (n=100000)
               Rate       dt       ts dt(cached)        tp ts(cached) tp(cached)
dt           2147/s       --     -28%       -43%      -96%       -98%       -98%
ts           2973/s      38%       --       -21%      -95%       -97%       -98%
dt(cached)   3750/s      75%      26%         --      -94%       -96%       -97%
tp          60976/s    2740%    1951%      1526%        --       -31%       -51%
ts(cached)  88496/s    4021%    2877%      2260%       45%         --       -28%
tp(cached) 123457/s    5649%    4053%      3193%      102%        40%         --
    # Subtest: Asia/Tokyo(+0900)
    ok 1
    ok 2
    1..2
ok 3 - Asia/Tokyo(+0900)
Benchmark: timing 100000 iterations of dt, dt(cached), tp, tp(cached), ts, ts(cached)...
        dt: 55 wallclock secs (54.70 usr +  0.11 sys = 54.81 CPU) @ 1824.48/s (n=100000)
dt(cached): 34 wallclock secs (33.92 usr +  0.06 sys = 33.98 CPU) @ 2942.91/s (n=100000)
        tp:  2 wallclock secs ( 1.61 usr +  0.01 sys =  1.62 CPU) @ 61728.40/s (n=100000)
tp(cached):  1 wallclock secs ( 0.79 usr +  0.00 sys =  0.79 CPU) @ 126582.28/s (n=100000)
        ts: 39 wallclock secs (39.50 usr +  0.13 sys = 39.63 CPU) @ 2523.34/s (n=100000)
ts(cached):  2 wallclock secs ( 1.79 usr +  0.01 sys =  1.80 CPU) @ 55555.56/s (n=100000)
               Rate       dt       ts dt(cached) ts(cached)        tp tp(cached)
dt           1824/s       --     -28%       -38%       -97%      -97%       -99%
ts           2523/s      38%       --       -14%       -95%      -96%       -98%
dt(cached)   2943/s      61%      17%         --       -95%      -95%       -98%
ts(cached)  55556/s    2945%    2102%      1788%         --      -10%       -56%
tp          61728/s    3283%    2346%      1998%        11%        --       -51%
tp(cached) 126582/s    6838%    4916%      4201%       128%      105%         --
    # Subtest: America/Whitehorse(-0700)
    ok 1
    ok 2
    1..2
ok 4 - America/Whitehorse(-0700)
Benchmark: timing 100000 iterations of dt, dt(cached), tp, tp(cached), ts, ts(cached)...
        dt: 57 wallclock secs (56.63 usr +  0.11 sys = 56.74 CPU) @ 1762.43/s (n=100000)
dt(cached): 36 wallclock secs (35.81 usr +  0.06 sys = 35.87 CPU) @ 2787.84/s (n=100000)
        tp:  2 wallclock secs ( 1.67 usr +  0.01 sys =  1.68 CPU) @ 59523.81/s (n=100000)
tp(cached):  1 wallclock secs ( 0.80 usr +  0.00 sys =  0.80 CPU) @ 125000.00/s (n=100000)
        ts: 40 wallclock secs (40.52 usr +  0.14 sys = 40.66 CPU) @ 2459.42/s (n=100000)
ts(cached):  3 wallclock secs ( 2.06 usr +  0.01 sys =  2.07 CPU) @ 48309.18/s (n=100000)
               Rate       dt       ts dt(cached) ts(cached)        tp tp(cached)
dt           1762/s       --     -28%       -37%       -96%      -97%       -99%
ts           2459/s      40%       --       -12%       -95%      -96%       -98%
dt(cached)   2788/s      58%      13%         --       -94%      -95%       -98%
ts(cached)  48309/s    2641%    1864%      1633%         --      -19%       -61%
tp          59524/s    3277%    2320%      2035%        23%        --       -52%
tp(cached) 125000/s    6992%    4982%      4384%       159%      110%         --
1..4

TODO

リファクタリング

スキマ時間でえいやと実装を進めてしまいがちで、カオスになってきたので整理する。

テストケースの追加

なんか微妙なバグを拾いきれてないきがするのでテストを整える。

サポートするべきフォーマットの選定/実装

他の実装のstrptimeを見てみて必要があらば実装する。

ドキュメンテーション

サポートしているformatの説明と形式、localeの変更のしかたなどを説明する。

リリース

https://github.com/chansen/p5-time-moment/issues/4#issuecomment-36947533
有益っぽいのではやくリリースしたい。

XSによる高速化

Time::Localの実行が遅いので互換制のあるインターフェースでXS版を書いてみる。

XS実装の作成

libcのstrptimeだと%z/%Zがparse出来ないので、結構頑張る必要がある。(たぶん作らない)

[追記] timezone処理の環境依存を切る

http://www.iana.org/time-zones のあたりからいい感じにモジュール化して別のpackageで出してそれに依存させる。

*1:/usr/share/localeなどに依存

*2:やめたい