時計を壊せ

駆け出してからそこそこ経った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:やめたい

ORDER BY狙いのキーが何故速いか

どの最適化が効くんや…とググった。
以前も調べた気がしたが思いだせず、ひたすらググる羽目になったので、
反省してブログに残す。ふつーにmysqlのdocumentに書いてあった。


http://dev.mysql.com/doc/refman/5.7/en/limit-optimization.html

If you use LIMIT row_count with ORDER BY, MySQL ends the sorting as soon as it has found the first row_count rows of the sorted result, rather than sorting the entire result. If ordering is done by using an index, this is very fast.

バージョン古いけど日本語のほうも同じ内容書いてある。http://dev.mysql.com/doc/refman/5.1/ja/limit-optimization.html


というわけで、LIMITを付けていてかつORDER BYにINDEXが使われる場合は、
row_countぶん発見したらソートfetchを中断して結果を返すので、
rowsが結構デカくなってもLIMITがバカでかいとかでない限りだいたい速いということっぽい。
this is very fast. と言い切るあたり、かっこいいとおもう。


元ネタ: http://togetter.com/li/564015http://www.slideshare.net/yoku0825/devsdba


追記: MySQL Performance blogに詳しく書いてある記事があった
http://www.mysqlperformanceblog.com/2006/09/01/order-by-limit-performance-optimization/


追記2: covering indexにならなくとも最適化は効くと思ってたので、検証してみます。分かったら追記します。
b+tree indexはsort済みなのでsortしなおす訳がなくて、たしかに「ソートを中断」より「fetchを中断」のほうが正しかったです。

#yokohamapm #10 に参加しました

ビールおいしかったです。スポンサーのKAYACさん、準備・運営して頂いた皆さんありがとうございました。


こんな感じの内容を話させて頂きました。


要約すると、既存のライブラリ、罠が多くて理解と慣れを要求されるけど、
Time::Momentがだいぶ良い感じな雰囲気なので、使っていこうよ。という感じの内容です。
strptime相当の機能が無いので、strptimeだけの機能を提供するモジュールを別モジュールとして用意すると良いんじゃないかなと考えています。
epochだけ返せば良いかなと思いつつ、offsetの情報がformatから失われるといろんなoffsetを含むformatをparseするケースでつらそうなので、
offsetの情報も返したほうがいいかなー。と思いつつ、どうなのだろうか。


karupanerura/Time-Strptime · GitHub
karupanerura/Time-Strptime-libc · GitHub


Time::StrptimeはApache::LogFormat::Compilerの個人的にeval compile hackと呼んでる実装を参考にしたんだけど、
そのへんでpure perl codeの高速化に関する知見が得られたのでこれは別途どっかに記事を書いたりはなしたりしたい。


あと、JSON Schemaおもしろいなーと思ったり、IRKitすごいっておもったり、DBD::mysql奥がふかいっておもった。
JSON Schema活用すると、client sideとserver sideでvalidatorのロジック/ルールを共通化できたり、
仕様書にもそのまんま使えて、実装と仕様書の差異を気にしなくてよくなるなと思ってり夢がひろがった。
最近、いにしえの技術について学び直したりもっと基礎的な部分の知識を補強したりしていて、最新の技術にすこし疎くなりがちなので、ちゃんと追っていきたいなーとおもった。

クソコード、あるいは技術的負債

クソコードについてここ数日で考えたことを書いてみる。

技術的負債まわりのえらいひとたちの議論を眺めてて、技術的負債って言うとなんかプロっぽいけど、クソコードって言ったほうが示したいモノを素直に表してるし分かりやすいきがしてきた。

 

 

クソコードを書くなとは思わないけど、クソコードをいつまでも放置するのはやめようって思う。

クソコードは次なるクソコードを生み出すし、バグを隠蔽するし、メンテナンスコスト増大の悪循環のキッカケになるし、新人の教育上良くないので無くて済むならもちろんないほうがいい。

 

 

ただ、ギークな人たちを除いて、さらっと60点*1のコードなんて書けない。僕を含め大多数のエンジニアは自分自身が書いたクソコードをリファクタリングして60点以上のコードを目指すための時間が必要になる。

そのうえ、そういうコードを書いてもだいたい時間経過に伴って事情が変わって、60点のコードの挙動を壊さないように慎重に書き換えてビジネス上の目的を達成していったら、どんどんコードはクソになってくとか、ザラだと思う。

そういうわけで、クソコード書くなって言っちゃうと、生産性*2は落ちると思う。

それに、だいたいビジネス上の目的を達成するためにはアホみたいに短い締め切りを守る必要があって、さもなくばチャンスを逃してしまい予算が達成できない。あるいはプロジェクトの継続が困難になってしまう。あるいは経理上ヤバイ数字になってしまってどうなってんだと株主から怒られる。あるいはユーザーに呆れられて製品から人が離れていく。いろんな最悪のシナリオが考えられて辛い気持ちになる。

そんなときはどんなにクソなコードを量産しようとも、ビジネス上の目的を達成しないといけない。いま自分の給料を稼いでくれているシステムはクソコードも厭わないビジネス上の目的を達成するための努力の結果出来上がったものかもしれない。

もちろん、最初からクソコードなしでそういうビジネス上の目的が達成出来れば最高だと思う。そこを目指すこと自体に異議は無い。

ただ、クソコード書くなって言っちゃうとビジネス上の目的を達成する事が(ただでさえ困難なのに)、より難しくなってしまう。

だから、クソコード書くなって言うのはやめた方がいいと思う。間に受けてひたすらコードを綺麗にするために命を懸けるみたいな事をする人が生まれかねない。*3

ただし、新人の場合は「こういうコードを目指せば良いのか」と思ってクソコードを量産するように育っても、自分達も本人も困る事になるのでちゃんとレビューしてクソコードは理由を説明しつつ突っぱねて良いコードが書けるように教育していくべきだと思う。

要するに、僕が思ったのは、やむなくクソコードを書いても、それで目的が達成出来るならいいじゃないか。ということだ。

 

 

ただし、クソコードはクソコードのままメンテナンスし続ける事は出来ない。

なぜなら、メンテナンスコストが増大し、ビジネス上の目的を達成するための変更を加えるのにかかる時間が増えていってしまうからだ。*4

クソコードの度合いにもよるが、時間経過に対して正比例的にメンテナンスコストは増大すると思う。(このへんは技術的負債が云々でホッテントリしてた人の記事が詳しく書いてたので読むといいと思う。僕はその記事にすごく共感した。)

だから、クソコードは直していかないとそのシステムでお給料を貰うのはどんどん困難になっていくと思う。

クソコードをメンテナンスし続けても給与3倍から遠ざかって行くし夢がない。

なので、クソコードは少しづつ改善しながらメンテナンスし続けていくのがいいと思う。これは最初からクソコードを書かないって事より遥かに難しいけど、給与3倍になると思えば出来ると思う。疲れてきた。

 

 

そして、時間的制約の中でどれだけ良いコードが書けるかというのは非常に大事だと思う。

クソコードは止むを得ず書くもので、クソコード自体は良いものではないので、書かなくて済めば書かないほど良い。

だから、スケジュールに影響が出ない程度に良いコードを目指すのはとても良い事だと思う。クソコードをちょっと直して60点以上になればビジネス上の目的を達成した上でコードは綺麗に保てるし最高だ。誰もが幸せになれる。

ただ、ぼくたちはだいたい未熟だしいつだってどんな仕様だって60点以上に出来る実力はないから、良いコードを書けるようになるために常日頃から努力を重ねる必要があると思う。だけど、ビジネス上の目的を達成出来ないと給与3倍から遠ざかっていくし、バランスが大事だと思う。その上で良いバランスを目指すために良いコードをさらっと書けるよう努力していくとみんな幸せになれそうだ。

 

 

iPhoneでベットでぬくぬくしながらてきとーに書いてたらつかれた。今日は有給だ。

*1:人によって基準は違うし、だいたいクソコードの基準を決めるのはギークな人たちなので、ここでは便宜上60点が合格点であると定義する

*2:時間あたりでどれだけの成果物を生み出せるかという意味合いでの

*3:もちろん、それはそれで良い事なんだけどビジネス上のメリットは殆んど無いケースが多いと思う

*4:そんな変更を加える必要も無いくらい完璧にシステムが作られているなら別だが、そうそうないでしょう

redis-sentinel管理のredisを手動failoverさせる

やんごとなき理由でredis-sentinel管理のredisのmasterを再起動したくなって、
手動failoverのやりかた調べてたら公式のドキュメントにちゃんと書いてあった。
公式のドキュメントちゃんと読まないとダメだ。


適当なsentinelに入って、

redis 127.0.0.1:26379> SENTINEL failover <master name>

としたら良い感じにfailoverしてくれた。
redis-sentinelべんり。

configファイルのmasterのIPも新しいものに変えておかないと次回sentinelを再起動したタイミングで狂うので忘れずに換えて置かなければならないのだけ注意したい。
というか先にconfigのIP変えてから手動failoverしたほうがいいかもしれない。

参考: http://redis.io/topics/sentinel

2013年のKPT

2013年終わったのでKPTやってみる。

Keep

新しい分野に挑戦した

  • Android
  • フロントエンド開発
    • こっちはお手伝いとお遊び程度だけど

新しい言語に挑戦した

新しいモジュールを作った

  • Parallel::Async
  • SQL::Maker::Plugin::Case
  • Hash::Compact::Lite

ISUCON3出た

  • 予選通過
  • 本戦惨敗

作曲した


ぼくの干し椎茸 by karupanerura on SoundCloud - Hear the world’s sounds

帰り道(demo) by karupanerura on SoundCloud - Hear the world’s sounds

帰り道はこのデモをもとにThe Monzllisでバンドバージョンを作った。そっちは2番もある。

ライブやった

高円寺界隈の人と仲良くなれた

  • バンドつながりで知り合いがふえた

資格試験受けた

温泉発火村やった

  • 熱海行った
  • 旅館がすばらしかった

地域PMに参加した

  • Hachioji.pm
  • Chiba.pm

エンジニアのコミュニティにより深く関われた

  • kenjiskywalkerさんとかstudio3104さんとかuzullaさんとかxtetsujiさんpython_spameggsさんとかsongmuさんとさyusukebeさんとか、挙げてくとキリが無いくらいの人と知り合えた。

ITエンジニア平成会に参加できた

  • 意外とあの人と歳近かったのか!みたいなのあってよかった。
  • 新年会やりましょう

Problem

デスマった

  • 見積もりミスこわい

引っ越しの片付けが済んでいない

  • 言い訳のしようも無い

Yomiuri完成してない

  • Text::Markdown::Hoedownのlow-level APIを使って組んでたがAPI変更があったり

ISUCON3惨敗

  • インフラ周り本格的に勉強したいしノウハウ積みたい

情報セキュリティスペシャリスト試験落ちた

  • 午前Iでマークシート写し損ねがあって減点したのが痛かった
  • 自己採点だとたしか午前Iだけ落ちてたはず。かなしい

IT芸人になりつつある


自分が作った代表的なプロダクト的なものが無いまま名前だけそこそこ知られてるって状況になりつつあってエンジニアとしての危機を感じている。

Try

新しい言語を会得する

新しい分野に挑戦する

  • サーバー運用
  • 言語処理系

新しいモジュールをつくる

  • ORM
  • 痒い所に手が届く系

遠方の地域PM/参加した事の無い地域PMに参加する

  • Hokkaido.pm

Perl/MySQL以外の勉強会に参加する

  • rbとかgo

英語を勉強する

  • YAPCで英語でトークしたい

数学を勉強する

ソロでライブやる

  • 弾き語り
  • のために曲作る