エレガントな割り勘について
やり方
前提
- そこそこ仲が良いメンバーであること
- 今後もお付き合いがあること
手順
- 各人、好きなだけ場にお金を出す
- 各人、かかったお金を取る
- 差額のお金をどうするか決める
詳細なやり方
- 各人、好きなだけ場にお金を出す
- 自分なら、そのアクティビティにいくらまで払えるかを「自分で」値付けして、支払う
- 人から強制されてはいけない
- 払いたくなかったら0円でも良い
- 各人、かかったお金を取る
- 自分が立て替えて支払った額を最低額として、実際にかかった労働力も含めてもらう
- 「交渉が難航した」「トラブルを解決した」などがあれば、多めにもらって良い
- 他の人は、監査する
- 多めにもらいすぎていたら、否定する権限を持つ
- 揉めたら領収書などの証拠をもとに、適切な額を決める
- 自分が立て替えて支払った額を最低額として、実際にかかった労働力も含めてもらう
- 差額のお金をどうするか決める
- 足りなかったら再び徴収する
- あくまで払いたい人が支払う
- それでも足りなければ、強制的に徴収する
- あくまで払いたい人が支払う
- 多かった場合、山分けするなり、全員にアイスでも奢るなりで余ったお金を消費してしまう
- 足りなかったら再び徴収する
主張
かかる時間の短縮
大幅に短縮できるはずである。 1円単位で経費精算しようとすると、割とめんどくさい計算をしないといけない。
この方法では、やることはシンプルで、適当にお金を集めて、適当に分配するだけである。細かい計算は一切しない。
複数人で何かやることには、価値が生まれる
基本的に、複数人で何かすることは、一人で何かをするよりも、価値があるものとすることができる。なぜなら、もし一人で何かする方が価値が高いなら、参加者が各々で別々にそのアクティビティをするべきだからだ。(その場合は、経費生産も発生しない。) 一人でやるとある額払っても良いと思えるものに、複数人でやると、ある額+αの額を払っても良い、と考えられるはずである。
その+αが、この手法が成り立つために必要な源泉である。+αが増えれば増えるほど、最終的にお金が足りなくなることはなくなるはずである。
納得できる支払い額は、人によって違う
お金の額は誰にとっても同じ絶対的なものだが、お金の価値は相対的なものである。年収100億円の人の1万円と、年収1万円の人の1万円は、生活費換算で、100万年分と1年分になっている。収入100億円の人にとっての1万円は端金だが、同じ期間での収入が1万円の人にとっての1万円は大金である(場合が多い)。
経済力の高い人と低い人がいて、「自分ならこの額くらい払っても良い」という額はそれぞれみんな違っているはずである。で、そのそれぞれの額で払うことは、全員納得できる。
かかった経費よりも集まる額が少ないケースは、そんなに多くない
- お金がない人(学生など)が多いケース
- これは仕方がない、お金を持ってる人ができるだけ出す、それでも厳しい場合はお金がない人にも出してもらう
- あまりにも厳しい場合は、参加をご遠慮いただく
- これは仕方がない、お金を持ってる人ができるだけ出す、それでも厳しい場合はお金がない人にも出してもらう
- お金を出したくない人がいるケース
- 意地悪としてやっている場合
- 単に害悪なので追い出すべき
- トラブルなどに巻き込まれるなど、ネガティブな感情になっている場合
- 仕方がない
- 意地悪としてやっている場合
- 経費がかかりすぎたケース
- 減らせ、としか言えない
全員の納得が得られる
全員支払う額に納得できる額を支払っているはずなので、支払う額について納得しているはず。(納得できないのであれば減らせば良い)
自分が立て替えた経費分はもらっているはずなので、もらう額についても納得できるはず。(納得できないのであれば増やせば良い)
「支払う額が少なすぎる」不満や、「もらう額が多すぎる」不満がある場合、それぞれで話し合って解決していただきたい。
起こりうるトラブル
絶対的な額で決めるのに比べて、ギスギスするかも
絶対的な額というのは、公平ではないが公正ではある。それをそれぞれの経済力に応じた公平にすることで、公正ではなくなる。
今後も関わりがない人同士だと、「1円も支払わない」のが最善手になっている
前提は必ず守る。 縁の切れ目なので、もう関わらないが吉。
サークルでISUCONを開催した話
前置き
そろそろサークルの後輩くんたちにも強くなってもらいたいなーって思って開催した。
思えば、あれは何年前だろう(7年か8年か)、僕もサークルの先輩にサークル内ISUCONを開催してもらって、「へーこんな競技があるんだ」って知った。 そこから毎年参加していて、決勝にも行ったりして、競技中練習中通して様々な技術を学んだおかげで、今があると思ってる。
在学中にも開催したりしたが、何分自由なサークルなので、最近は開催されてなさそうだった。 ちょうどGWに空いた時間が取れそうだったので、完全初心者でもサポートできるだろうと踏んで、開催してみました。
開催前
サークルのDiscordにて、ISUCONを開催したら嬉しいか、参加してもらえるかを聞いてみたところ、参加してくれる人がいそうだったので、開催することにした。 サークルとは、信州大学公認サークルkstmのことで、信州大学学生によるものづくりサークルである。 なにかものを作ったり、こうやってコンテストをやったり、LT大会をやったりと、ITを中心として様々なことをやってるので、興味がある信大生は是非話を聞いてみてください。
(もしkstm関係者の人でDiscordに入ってない人がいたら、部員募集のメーリスまで)
問題セットはISHOCON1を使うことを決めた。 ISHOCONは一人で参加するのにちょうど良い難易度になっていて、本家ISUCONに比べて問題がシンプルである。 今回は初心者向けでもあったので、できるだけシンプルな問題が望ましかった。
がっつり改善できるように、また時間を区切ってやると参加できない人も出てきたりすることが考えられたため、今回は4/29~5/9という長期間開催することにした。 結果的には想定通りに進みよかったのだろうが、一方でもっとバチバチした戦いを見たさもあるので、短いほうがよかったのかもしれない。
運営には僕と、同じくサークルOBでISUCONチームメイトのあーちゃんに入ってもらった。
AWSを使用してリソースを用意することにしたので、terraformを書いた。 そのterraformは、今回運営してみて、安定して使用できることの確認が取れたので、PRにしたところ、マージしてもらえたので、今後主催する人には是非活用してもらいたい。
terraform -> ISHOCON1/terraform at master · showwin/ISHOCON1 · GitHub
開始
4/29 09:30 頃、開始した
やったことはterraform applyしてリソース作成、できたEC2のIPアドレスと問題のマニュアルを参加者に配布のみで、事前準備の成果もありとてもスムーズに始められた。
SSHログインできないなどのトラブルがあったりしたが、対応した。
少しずつベンチマークを回して点数が付き始めた。
一気に点数を伸ばし始める人が出てきたり。
この頃から運営も特にトラブルなどもなく、暇になったので、参加することにした。
時々質問が来たら答え、次にやるべきことをアドバイスしたりして過ごしていた。
あと、運営としてやったことといえば、
点数がインフレして、下の方でがんばりが見え辛いとの意見から、
対数表示にグラフを変えることで、点数をわかりやすくした。
結果発表
順位は以下のようになりました
- Goryudyuma
- bgpat
- Noiri
- kitoriaaa
- fono09
- kitakou0313
- ritsuxis
- togacoder
- arsley
- zipnofastener
(提出が一度でもあったアカウントのみの集計です。)
まとめ
AWSの料金
計$201 でした。
開催期間中、c5.xlargeを参加者の人数分spotで立て続けたのが、値段の大半でした。
時間を絞ったり、インスタンスサイズを下げれば、もっと安く開催できると思います。
(数日にわたって開催するとか普通はしない)
終わりに
ISUCONは順位よりも、コンテストで何を学んだかが重要です。 参加した人は、是非学んだことを糧にして、将来強いエンジニアになってください!
参加してくださった方、問題を作問・公開をしてくださっている @showwin さん、運営を手伝ってくださった方、皆さんありがとうございました!
参加エントリ
Goryudyuma
Noiri
fono09
kitakou0313
ritsuxis
スペシャルサンクス
問題
ポータル
退職エントリ(仮)
今日5/31をもって、前職を退職し、6/1から別の会社で働くことになりました。
本当にいいチームで、いい経験、いい仕事ができたと思っています、お世話になりました、ありがとうございました!
本物の退職エントリはすでに書いていて、前職に公開するにあたって問題がないかのチェックを依頼していており、OKが出次第公開いたします。
本物の退職エントリでは社名が出ている上に、よかったことも改善できるなと思ったことも書いていて、万が一にも揉め事などにはしたくないため、一応チェックしてもらう、といった感じです。
(NGが出た場合はごめんなさい、お蔵入りするかもです。)
これからも変わらず、エンジニア人生を歩んでいきますので、よろしくお願いします!
そして、次の職場の皆さん、これからよろしくお願いします〜!
サークルでISUCONを開催したので、参加した話
自分で開催したISUCONに自分で参加してきた
運営側視点の記事 -> TODO: 後で貼る
作業用リポジトリ
注意
問題セットは、@showwinさんのISHOCON1です。(@showwinさんありがとうございます!)
この記事はISHOCON1のネタバレを多大に含みますので、ご注意願います。
やったこと
- 【5/1】
- あーちゃんが始めてるのを見て、やるかーってなってインスタンスを立てる
- gitに沈める
- 初手、usersをメモリに持たせる
- 正直これはいい手ではない
- なんの計測もせず、特に理由なくメモリに載せる判断をしているから
- ユーザーが増えたり減ったり、パスワードが変わったりしないことをまず確かめて、そこがボトルネックになってるってわかってからやるべき変更
- 何回かこの問題をやったことがあり、その時の淡い記憶が残っていたからできた判断
- 正直これはいい手ではない
- 適当にindexを貼る
- SQLから
SELECT *
をなくすSELECT *
は基本的に絶対悪- 何を取りたいクエリなのかわからないのでチューニングしづらい
- カラムが増えたり減ったりすると死ぬ
- 無駄な通信や無駄なdisk accessが発生する
- idをLIMIT OFFSETで取っているところを直す
- productsは増えたり減ったりしないので、pageの番号からproduct_idは確定できる
- 大体2万点くらい
- 何故かここでunix domain socket化してる
- 将来的には点数が上がるところではあるし、やっても損ではないが、このタイミングか?と言われると正直微妙
- template.Mustを起動時にして、毎リクエストごとにテンプレートを見に行かないようにする
- pprofを仕込む
- この辺から、ちゃんと計測しようとしてる意思が見える
- pprofは神ツール
- pprofで、byteをruneに変換してるところが遅いと出たので、できるだけ変換しないようにSQL側で最低限だけ返すようにした
- 大体4万点くらい
- N+1の解消
- productごとにcommentを取ってきてくっつけていたが、最初にcommentの必要な分を一回のクエリで取ってくることで、50回もクエリをたたかないようにした
- 11万点くらい
- 10万点の壁を超えられて、この時点で1位だったので一旦終わり
- 大体5時間くらい?
- インスタンスを消した
- 【5/3】
- あーちゃんに負けてて悔しかったので、第二部始める
- インスタンス建て直しから
- Goのhtml/templateは遅いので、パッとググって出てきたquicktemplateに書き換え
- 解析してバイナリにあらかじめしてくれるらしく、20倍くらい早いらしい
- https://github.com/valyala/quicktemplate
- 15万点くらい
- productも増えたり減ったりしないのでメモリに持たせた
- 18万点くらい
- productのdescriptionも、70文字以上だったら切る処理が重かったのでメモリにのせた
- workloadが40くらいが安定することに気づく
- 20万点くらい
- また一位に返り咲いたので、ここで終わり
- インスタンスを消した
- 【5/5】
- あーちゃんに抜かれてたのはまあいいとして、Noiriに抜かれてたのはめちゃくちゃ悔しかったので、もうちょっと頑張ることにした
- Noiriめちゃくちゃ強くなってて凄かった
- もうちょい経験値が貯まれば最強になりそう
- Noiriめちゃくちゃ強くなってて凄かった
- historyをメモリに持たせる
- どんどんメモリに乗っていく・・・
- 23万点くらい
- commentをメモリに載せる
- この時代に作られたISUCONの問題は、メモリに載せるのが正義な奴が多いですよね
- 対策として、複数台構成や、メモリに載せにくい問題セットになったりしていく
- この時代に作られたISUCONの問題は、メモリに載せるのが正義な奴が多いですよね
- 36万点くらい
- commentもruneに変換してから長かったら切る、という動作が重かったので、先に切っておく
- 41万点くらい
- この時点で、DBから読み取りがなくなり、書き込みだけになった
- indexを全て消して、INSERTに特化する
- クエリはprepareを使うようにする
- あらかじめクエリを先にDBに投げて、クエリに使う値だけ後で投げる手法
- 42万点くらい
- 一位になったので、満足してインスタンス消した
- 【5/8】
- あーちゃんに負けていた。それはいいとして、試してみたい手法を思いついたのでやってみる
- pprofによると、session周りでかなり重そうだった
- ginのsessionに、memstoreなるものがあることを発見
- pprofでみる限り改善されてそうに見えるが、点数的には上がらなかった
- ふて寝するためインスタンス消した
- 【5/9】
- quicktemplateでも遅いって出てたので、生成されたHTMLをキャッシュすることにした
- 少しだけ点数が伸びた
- 44万点くらい
- 僅差で1位でフィニッシュした
この後できること
もうやれることを全部やり切った感があって、特にない気がする。細かい改善を積み重ねれば10000点くらいあがるかもしれないが、そこまでいける気もしない。
そもそもベンチマーカーのCPU使用率が150%を超えていて、もうどうしようもない感が出ている。
感想
- やり切ったので満足!
- 開催して良かった
- 強い後輩たちが育ってくれるといいなぁ。
- また開催できたらいいな!
- (次の運営、誰かしてくれないかなー?)
ISUCON8予選で惨敗してきた話
ISUCON8に、 @bgpat_ (あーちゃん)と @Owl_Works (おうるさん)と三人で出て、惨敗してきました。
あーちゃんのブログ記事
前日まで
チームメンバー集め
まず、今年から僕は社会人枠になってしまったので、社会人枠で出てくれるメンバーを探す必要がありました。とりあえずあーちゃんに声かけました。
@p1o1c1k1y ところであーちゃん、今年ISUCON出る?会社の人と?
— 僕はロバではなく馬 (@Goryudyuma) July 25, 2018
社会人枠になりそうで、チームメイト見つけるのがめんどくさいので開いてるなら僕に拾われて
まだ決まってないので喜んで拾われますん
— あーちゃん (@p1o1c1k1y) July 25, 2018
一人目ゲット。
二人目、誰にしようか。
うぇーい。あと一人どーしようね。
— 僕はロバではなく馬 (@Goryudyuma) July 25, 2018
決まらなかったら、ISHOCONで誰か拉致ろう。 @showwin さんとか。
— 僕はロバではなく馬 (@Goryudyuma) July 25, 2018
主催者拉致るは草
— あーちゃん (@p1o1c1k1y) July 25, 2018
※@showwinさんはISHOCONの主催者兼作問者
なんか最近CTOになって、ISHOCON作って遊んでいる人だけど、ISUCONは決勝出たことない弱者なので誘わないほうが良いですよw
— Shogo ITO (@showwin) July 25, 2018
今年は会社の人と出ることになりそうなので、来年とかまた機会があったら誘ってくださいな!
断られる。残念。来年は是非!
ISHOCON2当日を迎える。
ISHOCON2で遊ぶ。
帰ってからISHOCON2についてと、メンバー募集の求人ブログ書く。
おうるさんもメンバー求めていたらしく、声がかかる。
無事三人チーム結成される!
チーム名決め
メンバー三人、僕が馬であーちゃんが犬、おうるさんがフクロウだよねって話になった。
他にいい案も思いつかなかったので、「Bremen」 に決まりました。
練習会
YahooのLODGEに集まってISUCON7予選を、それから夜中にISUCON4予選をやって、当日の流れとかをやりました。でも、明らかに練習量が足りなかった。
お互い何ができるか、どんなことが得意か、どんなことなら任せられるか、どれくらいやればどれだけ体力が残ってるかなどなど、チームメンバーのステータスを知り、お互いを信頼して。こういったことができてない(できない)即席チームは、とても弱いです。うーん、来年以降、本気で決勝目指すなら、そこからやらねばな。
当日
mixiさんのオフィスをお借りしました、ありがとうございました!
当日の流れとしては、あーちゃんのブログ以上のことは書けないので、省略するとして。ここからは反省タイムです。
まず、調査の結果GetEventがとんでもない回数呼ばれて遅いものであることがわかりました。
少し話は変わりますが、ISUCONって、学生枠での攻略法と社会人枠での攻略法は全然違うと考えています。具体的には、例えばボトルネックになるポイントが問題内に3つあるとして、学生枠で通るには2つ改善してあとを定数倍早くする、もしくは三つ改善する、くらいで通るというのが僕の肌感覚です。一方で社会人枠は三つとも改善は当たり前で+α何か改善した状態、くらいが社会人枠での突破ラインとなると思います。また、共通点としては再起動試験には絶対に落ちないようにすること、ってのが挙げられます。後は運です。
このように考えていたので、このN+1問題が解決しないと、根本的に点数は伸びなさそうだなーって考えからこのN+1問題に固執してしまい、さらに何箇所からも呼ばれている関数だったのに「それ自体を早くしないとどうしようもないよね」とか言って、分割は最低限で超大きいまま取り組んでしまいました。超大きいまま取り組むと解けないのは当たり前ですね。
また、最終的に三箇所全てに同じアプリケーションを配置する方針も少しあって、その制約のせいで例えばエンドポイントによって捌くサーバーを変える分割が気軽にできませんでした。これは完全に方針ミスです。
三箇所に同じものを配置する方針となった理由の一つは、最初のレギュレーションを読んだ時に、帯域が詰まってくるだろうと予想したから(三つとも同じものを配置してやれば帯域は三倍使えるという過去のISUCONの知見から)なのですが、そもそもベンチマーカーが一箇所にしかアクセスできない仕様と判明した時点でもう一度確認して、完全に頭の中から消し去るべきでした。調査に基づかない方針決めは本当に良くないです。
また、今回使ったデプロイ環境のせいでもあります。今回、ビルドはローカルでやってバイナリだけscpでサーバーに設置する、というデプロイのやり方でやりました。こうすることで外部パッケージの依存関係で問題を起こして本番環境を壊したりしない、同じ変更を三台のサーバーそれぞれに入れる必要はないので変更入れ忘れ事故がなくなる、それぞれ自分の環境でのビルドになるので作業中のコンフリクトが起きにくい、といったような十分なメリットがあるので採用されました。Golangはクロスコンパイルできるからこその作戦ですね。しかし、三台のサーバー全てに同じバイナリが設置されるので、それを念頭に置いた方針になってしまいました。
Redisに乗せる案もかなり初期から一応あるにはあったのですが、結局乗せ始めたのは中盤が終わりかけになってからという微妙な時間になってしまいました。これに関しても、Redis化に消極的だった僕が悪いです。Redis化に消極的だった理由は、初期ベンチマークから何かがエラーで、まともに全部Successでベンチマークが通ったことがとても稀だったからです。初期実装からエラーが出ていた理由がトランザクションがきちんと張られていなかったからっぽい?のですが、本番中はそこまでわからず、何だかよくわからないけど、例えばMySQLに入れる順番が大事(直前に練習したISUCON4予選問題)とかの隠れ仕様があり、結局Redis化しても点数伸びずに終わってもおかしくないなと思ったからでした。量が量でしたし、やるならもうRedis化に全て賭けるしかなかったですしね。それよりも確実にボトルネックとなるN+1問題の方が優先度が高いと考えていました。
こんな感じで、学生枠とは全然違った戦略を、特に練習時間も多く取れない状態で挑んだ結果の、当然の爆死でした。
感想
今年は問題が重すぎて辛かったですね。(ISHOCONの問題量に慣れてしまった可能性が微レ存・・・?)
あーちゃんはとてもデキるエンジニアなのですが、彼の作るインフラは本当に最高です。僕の方がついていけず、うまく使いこなせてないことが多かったです・・・。ただ、慣れてくると本当にいいものであることは間違いないです。
初めて組んだおうるさんも仕事がとても早くてびっくりしてました。いつの間にかタスク全部終わってる・・・みたいな感じでした。もしRubyならもっともっと活躍できたはずなので、本当にすごいエンジニアなんだなと思いました。
僕自身、Golangから少し離れていて、今ではたまにしか書かなくなってしまいました。もっと早く書けたはずなのになあ。精進が足りない。実装力をもっと鍛えねば。
足を引っ張ってしまい、二人には本当に申し訳ないです。
ISUCONはまともな環境でエンジニアしていると、どんどん弱くなっていく競技でもあると思っています。なぜなら、「永続層のDBとか、とりあえずぶっ壊してでも、なんとしてでも早くしよう」なんて思い浮かばなくなるだろうからです。まあ正攻法だけで勝てるほど強くなると、また違ってくるんでしょうけど。
そんなわけで、来年、どうなるかわからないですけど、本気で本戦目指すなら、ちゃんと練習して挑みたいですね。
ISHOCON2に参加して来たよ!
ISHOCON2に参加して来ました!
ISHOCONとは、@showwinさんが個人で提供していらっしゃる、ISUCONライクなコンテストです!チームプレイ推奨な本家ISUCONやその他企業コンテストとは違い、ターゲットは個人プレイなので、問題量もかなりライトに作ってあって気軽に挑戦できる割に、かなり奥深く作られているので、どんどんやるべきことが出てくる、点数もどんどん上がるという、やってて楽しいかなり面白いコンテンツになっています!
結果
今年も去年のISHOCON1に引き続き二位でした!
方針選定
まずは言語選定ですが、今回はひたすら楽しむ、遊ぶってことを目標にしていたので、勝つ目的で選んでいるGolangを使わずに、Crystalで挑みました!
Crystalって、僕の中で今かなりアツい言語なんですよ!Ruby文法な点が実はそんなに好きではない、ってこと以外は、型の扱いとかとても面白い上に、とんでもなく早く動いてくれるので、超オススメの言語です!
問題文やベンチマーカーを事前に読んでいたので、方針は行く前から決めていました。それは、「GETが来たらそこでHTMLを生成してNginxから返す、POSTが来たら生成したHTMLを全部消す」って方針です。
今回のISHOCON2では、ベンチマーカーは主に三つのフェーズに分かれています。
一つ目は事前テストフェーズで、きちんと想定通りの動きをしているかチェックされます。
二つ目は投票フェーズで、ひたすらPOSTがくるので、情報の更新を捌ききることができるかを問われます。
三つ目は確認フェーズで、ひたすらGETが来ます。ここでは情報の更新は起きないので、どれだけ大量に返せるかが問われています。
なので、POSTを高速に捌いた上で、GETは一回だけ生成してあとは静的配信に任せるってことをしました。
当日
朝起きて、泊めてあげた大学の友達と一緒に会場に行きました。
いつの間にやらCTOになられていた@showwinさんと会えました!
問題文やベンチマーカーなどは、事前に読んでいたので読む必要はほとんどなく、競技開始した直後は、環境チェックなどをしていました。
ベンチマーカーの挙動をある程度知っているので、まずはNginxにHTTP2を喋らすようにしました。
それから、cssをNginxから返させるようにしました。
そのあとは、Cystalでファイルを生成する方法を調べたりして、indexに来るGETリクエストを静的に配信できるようにして6万点超えで一位に、workloadを20まで上げて9万点まで上がりました。
そこでお昼ご飯、今回もとても美味しいお弁当が支給されます!
workloadが20程度では、CPUを使いきれていなかったので、上げられるように申請しました。
正直ここで、かなり余裕を持って一位を取っていたので、ガンガンやるのもなーってナメた感じでいました。実は事前に自分でやった時、計18時間くらいかけて5万点超えして終わってたんですよね。初見だと気付けない点も多かったし、後から考えれば方針もミスってたっていうのはあったんですけど、そこそこ自信がありました。
なので、すでに事前にやった点数の倍取れていて、さらにworkloadの上限が解放されれば、(ベンチマーカーの実装や挙動を知っているので)さらに点数が上がることを知っていたので、少なくとも競技時間内に、他の参加者で勝てる人いないんじゃないかなーって思っていました。
お昼からworkloadをいじったり、ちょっとした点数にほぼ関係のない変更をして、誰も伸びて来なさそうに見えたので、Service化とかしてました。実は15時頃時点での僕のコードは、ちゃんとSystemctlで再起動とかできたり、再起動時にmysql.serviceより後に起きて来るようにしたりとかして、今回は求められていない再起動試験があったとしても実は通るっていう変更とか入れてました。
悲報として、workloadを上げるとベンチマーカーが完走しないという問題が運営側で発生しており、何も変更しなくても点数が上がるというのは無くなりそうってことが判明しています。
その後、少しだけやる気が出て来たので、bootstrap.min.cssのgzip配信するようにしてみたり、index以外の他のGETに対してHTMLファイルを生成してNginxから返すようにしたりしていました。その結果、13万点くらい取れて、流石に大丈夫だろうと思っていました。(フラグ)
投票数のループをちゃんと数でもつ変更を入れると19万点まで上がったりしたくらいで、比較的まったりと過ごしていました。
オフィスから山手線と湘南新宿ラインが見えるのが本当にいいと思いました!
そうしているうちに、takonomuraさんが14万点を記録し、一気に追いつかれ出しました。「あ、これはまずい」と思いました。
本格的にISUCONすることを決意(←遅すぎ)、とりあえず投票データをメモリに持たせる変更を入れようと思いましたが、集計あたりがめんどくさかったので、ユーザーの投票数だけをオンメモリ化、その他メモリに乗せられるところは乗せたりしてたんですけど、結局この辺りは一回静的ファイルとして生成した後は必要ないので、そこまで効果は出ず、またINSERTを消すところまで行けなかったので点数も本当にわずかしか伸びず、っていう中途半端ところで終わりになってしまいました。
最後の方はCrystalのコンパイルが終わらない、vimが起動しない、なぜかインスタンスから追い出されて再度ログインできない、という謎の問題に悩まされていました。まあ競技なんで、不測の事態は起きますよね・・・。結局再起動試験は通るのか落ちるのかを確認できなかったこと、それからGitに残していたログをプッシュできなくなってしまったことが心残りです。ISUCON本番ではこのような問題が起きないことを祈りつつ、また自分の操作ミスでこのようなことが起きないようにしたい・・・。
その間にもtakonomuraさんは順調に点数を伸ばして、一位でフィニッシュされていました。
敗因?としては、慢心とそれに伴うやる気のなさ、後はエンジョイしようとして勝つ気が強くなかったことが挙げられます。はぁ・・・。
めちゃくちゃ楽しかったし、Golang以外でも全然勝負になるんやで!ってことも示せて、当初の目標は完璧に達成しているので、いいんですけどね〜。
その後は懇親会でお酒を飲んで、いろんな人とお話しさせていただきました。やっぱり懇親会は、ISUCONのメインイベントの一つですよね!同じ問題に取り組んだという一体感がすごい楽しかったです!
懇親会の後は、一年越しの焼肉に行きました!美味しかったし、とても楽しかったです!
さらに焼肉の後は、ISHOCON参加者の大学時代の友達と二次会?三次会?に行って、たらふく飲んで帰って寝ました。
前日まで
今回のISHOCON2には、実は事前にいくつかプルリクを送っており、マージされています。(なのでその過程で問題文や、ベンチマーカーの挙動などを知っていました。)
大きなものとしては、言語実装にCrystalとPHPとNodeJSを追加したりしてました。前のエントリにも書きましたけど、仕様のわかっているWebアプリを知らない言語で実装してみることは本当に勉強になりますので、オススメです!
僕がCrystalを使って、後一人NodeJSで参加してくださった参加者さんがいらっしゃいました。とても嬉しかったです!ISUCONではあまり使われないようなマイナー言語ばかりを追加しているので、実際に使っていただけるとはあまり思ってなかったので。
他の言語実装も、どんどん増えることを期待しています!
それから大きいプルリクとしては、ベンチマーカーのHTTP2対応をするプルリクですね。
Issue建てた時点で説明が下手すぎて、なんのことか理解してもらえなかったので、実装してプルリクを送って、それを見て理解してもらうというエンジニアらしい力技を使いました。
このIssueを建てた理由は、事前にISHOCON2の問題に挑戦していた時、どうしてもSSLの認証で詰まってしまって点数が伸びなかったんですよね。事前にやっている時なので、競技時間内ではない、ので、実装をいくら早くしても点数伸びないならベンチマーカーを早くしてしまえ!っていう、こちらもかなり力技でチートみたいなことをして点数を伸ばしていました。
埋め込みで使っている定数(workload *5 の5の部分)の値を変えるだけで、競技性が大きく変わってしまうのですが、かなり適当に決めた割には、うまく機能していたように思います。
http2にするだけで序盤から点数が爆上がりすることもなく、終盤なら気づいた人はかなり点数が上がるという、理想的な調整に偶然なっていたような気がしています。もちろんこの部分のパラメータもプルリク受付していると思うので、もっとこういう値にした方が楽しいと思うよ!という値などがあればお待ちしております!
最後に
@showwinさん、株式会社scoutyさん、参加者の皆さん、その他関わってくださった皆さん、ありがとうございました!
来年も多分開かれると思うので、よろしくお願いします!
ISUCONチームメンバー、後一人欲しいんですけど、見つかるんでしょうか・・・?もしよければ、声かけてください!
【追記】
見つかりました!というわけで、ISUCON8もよろしくお願いします!
ISUCON8、相方の @bgpat_ に加えて、助っ人の @Owl_Works さんとチーム名「Bremen」として出ることになりました!よろしくお願いします!
— そのうち退院する組長☆馬 (@Goryudyuma) August 26, 2018
(それから、久々の投稿、ありがとうございました。早く退学エントリ書かないと・・・。)
ISHOCON1をScalaで書いたお気持ち
アジェンダ
ISHOCON1の初期実装にScalaを追加したよ!
目次
ISHOCON1にScala実装を追加した
これの第二弾としてScala実装を追加してみました!
やっぱり、新しく学ぶ言語でなにか作ってみるのは本当に勉強になります。しかもできあがるものがもう仕様が決まっているので、ただただ実装すればいいだけというすごく楽な点がやはり魅力的です。
実装はRubyのものを参考に、フロントエンドはGoにあるテンプレートを参考に実装しました。前回のCrystal実装ではRubyのERBのテンプレートと実装はGoを参考にしたので、今回は入れ替えてみた感じですね。
開発環境
今回、このScala実装はWindowsのWSLとVSCodeを使って書いてみました。前回書いた記事は実は伏線でした。
簡単なアプリを書くには特に問題なく書けることがわかりました!
VSCodeでターミナルがだせるのもすごく良い点です。
ただ、やはり大規模なアプリになってくると、IDEによる支援がほしくなるなーって思いました。使い分けは大事ですね。
技術スタック
今回、Scalatraっていうフレームワークを使いました。ScalaでSinatraっぽく書けるやつですね。Ruby実装がSinatraで実装されていたので、同じように書ける物を選びました。ドキュメントが少し分かりづらいと感じた点以外は、概ねすごくいい感じに使えました!ドキュメントが分かりづらいと感じたのは、単に僕のScala力が低いだけな気がしてます。
テンプレートエンジンにはTwirlってのを使いました。普通に使いやすかったです。
他の言語による実装と合わせるため、O/Rマッパーは使わず、PlainSQLで書ける必要があると考えているので、データベース接続にはPlainSQLが書けるSlinkってやつを使いました。
それからこのscalatra.g8を使ってプロジェクトを立ち上げました。最初に必要なものを用意してくれるのですごく楽でした。
実装について
Rubyの実装を参考に書いたんですけど、実は少し後悔しています。結果としてすごく遅い実装になってしまったのはいくつか理由があるんですけど、そのうちの一つが動的型付け言語と静的型付け言語の違いです。
Rubyって普通の変数の顔をしながら平気でnilが入ってたりします。別にこれは悪い点ではなく、その特徴により非常に書きやすくなっているのですが、全く同じ動きをしたアプリケーションを静的型付き言語で書こうとすると問題が起きます。すべての変数をScalaで言うOption型にする必要が出てきます。
確実にnilが入っていないと確信できる時以外はすべてnilが入っているかもしれないと仮定しながら書くのは本当に辛いので、一部アレンジとして(ロジックは変えずに)改変しましたが、やりすぎて実装が別物になってしまうのも問題だというところが本当に難しいところです。
例
例を示します。
Ruby実装のloginのPOSTの処理だけを抽出しました。
- まず、authnticate関数に、nilかもしれないparams['email']とnilかもしれないparams['email']を渡す。
- authnticate関数のなかでは、nilかもしれないemailを用いて検索をかける。nil.to_sが""となるのでエラーにはならない。
- ユーザーを探すが、見つからなかった場合userはnilとなる。
- userがnilでなく、パスワードが入力されたものと正しければ次にすすめるが、だめならIshocon::AuthenticationErrorとなる。(仕様でパスワードが平文保存なのはISUCON的には本質ではないので見逃す)
- sessionにuserのidを入れる。
- current_userを呼び出す。このときのsession[:user_id]は、この関数だけ見ればnilかもしれないが、authenticate関数を通ってきているので確実にuser[:id]が代入されているのでおそらくnilではない。(が、それをこの関数を見ただけでは保証できない点が怖いといえば怖い)
- つづいて、update_last_login関数に、おそらくnilではないuserのidを渡す。
- そのユーザーのlast_loginを、現在時刻に更新する。
- redirectで/に飛ばす
と、こんな感じの処理になっています。うまくやってますが、nilが出てくる可能性が結構高いです。
Scalaでこれを実装する
さて、Scalaでnilっぽいことをやろうとしたら、Option型を使ってやる必要がありますが、先程述べたようにいちいち全部の変数をOption型に入れては取り出しってやっていくのは非常に辛い。
アルゴリズム的にNoneになるかもしれないuserは仕方ないとしても、私の実装では最初にauthenticate関数に渡すemailとpasswordはOption型でない普通のString型で渡すように変更したりして、うまく解決を図っています。
ここから少しネタバレになりますけど、authenticate関数でemailからuserインスタンスを作ったあと、user[:id]だけをsessionに保存したあと、userインスタンスを一度破棄し、その後でcurrent_userで先程保存したsessionの中のuserのidからまた同じuserインスタンスを作り出してるんですよね。ここ、authenticate関数がuserインスタンスを返せばそれだけでcurrent_user関数はいらなくなって、Scala実装的にもきれいに書けるんです。だけど、ISHOCON的には「ここでSQLを二回呼んでuserインスタンスを二回生成するのは無駄な処理なので、競技時間中に無くしましょう」的な意図も感じるんです。(深読みしすぎですかね?)。なので、ここのロジックを変えるわけにはいかず、Ruby実装に忠実に二回のSQL文とUserインスタンス生成をScala実装でもやっています。
ここが本当に重要なポイントで、今この実装になっているのは、作問者が意図して遅くしようとしてこの実装になっているのか、それとも普通に実装したらこうなってしまっただけでもっと早くしてもいいのか、区別がつかないんですよね。
普通のアプリケーション開発ではこの問題は起こりえないでしょう。というか問題にすらなりません。読みやすくもなり、無駄なインスタンスを生成しなくもなり、さらに高速化にもつながる、やらない理由はない案件ですから。しかし、ISUCONのクローンを用意している状況となると、意図してこの状況にしている可能性があり、その可能性が排除できない以上、無駄な実装をせざるを得ないです。
かといって、いちいち細かいところまで作問者に問い合わせるのも大変ですし、所詮は遊びでやっているので、自分の思ったとおりにやっています。
実装した結果
Scala実装が書けたあと、普通にベンチマークを回すと、ベンチマーカーの並列度が1と2の時は正の点数になりましたが、並列度を3以上にすると負の点数になってしまいました。ベンチマーカーのデフォルトの並列度は3なので、普通にベンチマークを取れば負の点数になっちゃいます。
例に上げたpostのloginの処理以外にも、Scala的でない実装になっているポイントがいくつかあって、それらを改善することで、(ロジックをそんなに大きく変えなくても、)並列度3でもおそらく正の点数が出るようになると思います。もちろんISHOCON的に遅くなっている部分を直せばもっともっと点数がのびるはずです!ぜひ頑張ってください!
終わりに
今回は気分が乗ったので、Scalaで実装してみました!これからも不定期に気分が乗ったときに、他の言語も追加していきたいと思います。
ISHOCON2も結構楽しみにしていて、最初にあったRuby実装に加えて最近Python実装も増えたのですが、まだGo実装がなくて試せてない感じです。やっぱり競技者として出る時は得意な言語で出たいですしね。じゃあお前が実装しろよって話ですけど、競技者として一度やってから書くほうが絶対にいいんですよね。あとから気付く、「ああ、あの時ああすればよかったのか」感が好きなので。なのでGo実装待ってます!(笑)
最後になりましたが、作問者の@showwinさん、楽しい問題をありがとうございました!