前にダミーデータサクッとつくりたいわっておもって、便利ストアドプロシージャつくったのでブログにも書いておく。
SELECTして結果をINSERTするところをコピペしていけば、ストアドプロシージャなんかつかわないでもいいんだろうけど、DRYなコードにしたいじゃん?
そういうことです。
前にダミーデータサクッとつくりたいわっておもって、便利ストアドプロシージャつくったのでブログにも書いておく。
SELECTして結果をINSERTするところをコピペしていけば、ストアドプロシージャなんかつかわないでもいいんだろうけど、DRYなコードにしたいじゃん?
そういうことです。
丸一日かかってしまったけど、ちゃんと作ってみた
DBIx::Sunny初めて使ったけど、シュッと使えて便利だった
今まで一人で一からアプリケーションを書くことは、あまりなかったから良い経験になった
それと共に、自分のPerlを使った実装力がないなーっておもってつぶやいた
@Maco_Tasu 地球人では二番目に強いくらいだね
— そば (@rg_gs) November 17, 2015
地球で二番目っぽいので満足でした 😇
ISUCON5の本戦をchatzmersとして参戦してきました。 結果からいうと惨敗でした。
敗因としてRobert C. Pike氏の下記の言葉に全て詰まっていると思います。
推測するな、計測せよ
今回使用されていたデータベースがpostgresでした。 僕たちは
以上の判断から最初にpostgresをmysqlに置き換えることをしました。 限られた時間しかない中、もしかしたらdbにボトルネックがないかもしれない という状況で先に移行をするという決断をしたのは判断ミスだったなと思います。
今回で言えばslowqueryをみたり、cpuの使用率をみたら今回はDBに全く 負荷がなかったことはわかっていたようなので、 予選の時にできていた計測して確実に潰していくというスタンスは本戦でも 徹底していくべきでした。
単純に移行に時間がかかりました。 やった作業と時間がかかったポイントとしては
以上の点で苦戦しました。
僕達ができたこととしては、
以上ができたこととなります。 複数台構成にしたので、単純にスコアが初期スコアの1000から約2倍の1900ほどの上がり successで無事11位になれました。
結果として力不足を露呈する形となりましたが、学びは多かったです。
僕らが現職で入社した時に行った社内ISUCONではスコアは良かったもののfailに終わってしまい、 スコアが残らなかったという苦い経験をしました。 それを加味して今回は、本戦が始まる前に確実にsuccessにして終わらせよう という話をしていたのでそれは有言実行できて良かったです。 二年半前からはちょっと成長ができたのかな...?
来年は上位目指して頑張りたいとおもいます。
運営の皆様本当にお疲れ様でした! ISUCONというイベントが行われる大切さと、参加することで得られる多くの経験を知ることできました。 様々な背景をもつエンジニアの方々一つの目標に向かって競い合う機会は滅多になく、そんな有意義なイベントが 今年も開催できたのは運営の大変な努力があったおかげだと考えると、感謝の極みです!本当にありがとうございました!
社内ISUCONには何度か参加したことあるものの、社外の人も参加するISUCONには初めての参加でした。 チーム名は「chatzmers」で、弊社の2013年新卒同期の @m0t0k1ch1 と @rg_gs と 僕 で出場し、最終スコア 21242 で予選総合4位で通過しました。
無事予選突破して、先輩エンジニアの方々と本選で戦えるのが本当に嬉しいです!
チームメンバーのブログエントリーです。 自分のエントリーより、詳細に丁寧に書かれているのであわせて読んでいただけたら幸いです。
ISUCON予選前までに、メンバーで集まってISUCON4の問題にGCPを使って挑戦することをしていました。 主な目的としては、実際にやってみてGCPに慣れることと、 予選で使うであろうミドルウェアまわりの設定の方法やツール等の使い方を予習することでした。
僕は2回あるうちの1回しか練習に参加できなかったので、前準備はあまり力に慣れなかったけど @rg_gsが予選で使うであろう自動デプロイのツールつくったり、supervisorの設定をおぼえる、リポジトリつくるなどし、 @m0t0k1ch1がGazelleやkataribe, pt-query-digestを使ってみたりなどをやってくれて頼もしかったです。 詳しくは2人が、ブログ書いたらこちらにもリンクをはります。
僕らが選択した言語はみんな大好きPerlでした。 そして最初に
をやってみて、「あーボドルネックだらけだな」ってなったので、
おもに重かった、/
と/friends
、/footprints
、/entries
を
すごいことをやろうとせず地道に直そうという方針で対応していきました。
主に自分がやった修正は一番多く叩かれてその上遅かった/
のまわりのコードの 改修で、
自分がした修正で効いたーってなったのは以下でした。
これらの修正でスコアが伸びて1位に躍り出て「これがISUCONか」と鳥肌たちつつ、わいわいしてました。
トップきたー!!!!
— MacoTasu (@Maco_Tasu) September 27, 2015
(その後抜かれたのは言うまでもないです)
他にもN+1問題を直してみたりといったアプローチを入れてみたものの、 クエリの実行計画かわって逆に遅くなったりなどあり思うように伸びず。 少し直してはベンチ回して検証して、良かったらmasterへmergeということを地道にしていました。
@m0t0k1ch1は事前準備の知識を活かしながら
をやっていました。 Gazelleに変えた変更や、MyISAMに変えた変更でだいぶスコア伸びててさすがって感じでした。 (事前準備のかいあってシュッと変更していた@m0t0k1ch1もすごかった)
@rg_gsは主に
などをしてくれていました。commit&pushしたら自動でデプロイしてくれるなどの地盤作りがあったおかげで 本当に快適ベンチ回すことができました。最高です。 また、entryのcommentの数をRedisに入れるのは地味に苦戦して、予選当日はつらーってなってたけど 先程@fujiwaraさんのisucon予選のエントリーブログを見て aofを使ってて、そういう手があるのかーってなりました。さすがです...orz
それぞれがやったことベースでまとめましたが、時系列順にどう対応していったかはきっと他のチームメンバーがまとめてくれるはず...。
あと書き忘れありそう
この2つができなかったので、またベンチかけれるなら挑戦してみたいです。
今回一緒のチームで参加した同期は、入社した時に社内ISUCONで皆FAILをするというつらいおもいを経験していたこともあり、 事前準備で予習と対策をたて、予選に臨んだことが結果としていい方向に事が運んだかなといった感じでした。 また予選前に、弊社の先輩エンジニアの@tkuchikiさんがalpというLTSVで吐かれたログをよしなに集計するツールを作っていらっしゃったので、そちらを使わせていただきシュッと遅いAPIを手軽に探ることが出来ました。ありがとうございます!
最後にこんなにやっていて楽しいと思える問題と、それに挑戦する機会をくださった運営の皆様、本当にありがとうございました!お疲れ様でした!
本選も頑張ります🙏
ふと気になったのでベンチマーク取ってみた。
#!/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の比較とかどうなってるんだってばよ ってなったのでちょっとだけコードみた。
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オブジェクト同士の比較は、闇っぽくて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
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倍ぐらいはでました。
golangっぽいコードに整形してくれるgofmtをcommit直前に実行し、 gofmtの結果追加の差分がでたら、1度commitを取りやめるようなcommit hookを書きました。 gofmtは差分があってかつ、拡張子が.goのファイルにのみ実行されます。
このコードを.git/hooks/pre-commit
に書いておけば、commit直前にhookされるようになります。
複数人でコードを書くような場合に、共通のhooksとしてそれぞれ設定おけば、 必ず整形されたコードのみがcommitされる良い状態になるのかなと考えて書いてみました。
※エディタをきちんと設定しようっていう話で済む気もしますが、別の試みとしてやってみた感じです。
ローカルPCで新しく追加したり、修正加えたテストを実行する時
prove -v t/hoge.t t/fuga.t ...
みたいに普通にやっていたんだけど、 git使ってるし、変更あったファイルをテストするのもっと楽にできるやんって思って
#!/bin/sh git diff --name-only HEAD t/ | xargs prove -v
こんな感じのshell script書いて、dp
ってうったら差分のあったファイルだけテストする感じにしみてた。
便利な気がする