ISUCON12 予選突破

チーム「と」最終31962点で11位。

github.com

方針

「まずは SQLite のまま進める。テーブル単位で徐々に MySQL に載せ替えつつも、ボトルネック解消に専念し1台でこれ以上大きく伸びなそう。と思うタイミングで複数台使う。大体16時ごろを目処」と思っていたが、結局上手くいかず何も MySQL に載せることはなく最後まで1台のまま進めることにした。また、最後の1時間で MySQL だけでも分けようとも思ったが、ログ出力を消したことで FAIL するようになったことの対応に追われて叶わず。

スコアを伸ばせたのは

  • MySQL ネックの解消
    • visit_history への index 追加と dispenseID の xid 化
  • SQLite に対する非効率なクエリの解消
    • visitor/player count と rank を都度計算せずに保存する
    • score の bulk insert

ぐらいだったと思う。

使ったツール

  • alp
  • pt-query-digest
  • pprof
  • top

やったこと

錯乱しながら作業していたのであまり正確ではない。

  • 10:00 - 13:00
    • 配信を見るのを忘れていて慌てて見る
    • コードを一通り読む
    • Docker 剥がし
    • デプロイスクリプトを用意する
    • visit_history に index を貼る
    • dispenseID で xid を使う
  • 13:00 - 14:45
    • pprof の有効化
      • MySQL ネックではなくなったので、この後 pt-query-digest は使わずに pprof と top だけ見ていた
    • 何も前進せず予選敗退の恐怖に支配され心が折れかける
  • 14:45
    • 妻に「切り替えてやれ」と凄まれ、自然と予選敗退の恐怖は消えさった。妻に感謝
  • 14:45 - 16:00
    • competition テーブルに visitor_count / player_count 追加
      • 大会終了時に計算して UPDATE
  • 16:00 - 17:00
    • ranking (competition_id [pk], ranking [pk], score, player_id [idx1], player_display_name) テーブルを追加して、ページングして取得できるようにする
      • /api/player/player/:player_id でもこのテーブルを使った
    • initialize が timeout するようになったので雑に並列に insert 投げて、sleep 2s している。sync.WaitGroup 使えよという感じがするが、FAIL を繰り返していたのでとにかく通したいという思いが先行した。
    • player_score を bulk insert にする
  • 17:00 - 18:00
    • go-json を使う
    • ログ出力をやめると to many files が出るようになったので LimitNOFILE を設定
    • keepalive や connection pool のサイズを変更したりしたが特段効果なし

改めて振り返ると無駄なことで時間を消費していたことが分かるし、やろうと思ったことは全然出来なかった。ただ、時間内でもっと出来たとは思えず、まあまあうまくいったほうじゃないかなという気がしている。

このままでは本戦を戦えなさそうなので、何度か練習しておこうと思う。

React Native を使う、型による支援がなくて不安、flow を使う。というのがなんか胃の中掻き回されてる感じがしてなんかしっくりこない。Web だったらたしかに良くなるねとなる。ネイティブアプリでも多分自分の場合はそのほうが効率的だろうな思うが、あべこべなことをやっているんじゃないか、どうせだったら型は静的に決まらないほうがいいんだ!と叫んで flow のようなものを使わないほうがまだ一貫性があるんじゃないかなどと考え出して上手くいかない。別に使うかどうか悩んだり迷ったりしているわけではなくて、たまにこういう風に思って結論が出ない。

Web っぽい UI

iOS で幅が可変のタグっぽい UI を作ってるとやりたいことのわりには結構大変だった。Web だと数秒でできそうだけど、他ではそんなに簡単でないものを Web っぽい UI と呼ぶことにした。Web っぽい UI を実装しないと行けないときに React Native だと大分楽に出来そうな気がしている。それとは別に Web っぽい UI を実装すべきかどうかはよく分からん問題がある。良い UI だから使われてきたものか手軽にそれなりに出せるから使われてきたものなのかがはっきりしない気がする。結果的に自分で決めるときは特に強い理由がなければ避けて、もう決まってる時は実装するという流れになっている。

  • Source Code Pro のほうが今っぽいかなという気がして使いはじめた
  • 横広すぎてやめたくなった
  • 思いとどまって 16pt -> 14pt にしたら見れるようになった
  • Ricty Diccord 16pt の Source Code Pro 14pt が大体同じ気持ちで見れる気がする

家族用に MacBook (Retina, 12-inch, Early 2016) を買って触っているととても軽かった。今持ち歩いている MacBook Pro (Retina, 15-inch, Early 2013) がめちゃくちゃに野蛮なものに見えてきた。多分 5年前ぐらいに Air を触ったのを最後に重いものばかり触っていたから感覚が狂っていた可能性がある。

Sierra で SandS

いくつかのリマッパーを試したけど、Karabiner on El Capitan での挙動を再現することができなかった。 自分の設定がよくないのか、そういうもんなのか分からないながらも「本当に Space だけを押した時のみ Space になり、Alt-Space や Control-Space が使えなくなる」もしくは「上手く動かない」という状態で、なんとか上手いことやれないか試していたものの諦めてSandS専用のアプリを作った。

github.com

前から github の 2段階認証の画面で「近くにデバイスを置け」ってでてくるけど、yubikey neo を置いたところで新しいタブで yubico のページが開くだけ(android chrome)で上手くいかなかったのでイライラしながら別の方法で認証していた。

Authenticating to GitHub using FIDO U2F via NFC - User Documentation

ということで Google Authenticator 入れたらできた。

挨拶

近所を散歩しているとよく日焼けしたオヤジがバイクから降りて、葬儀屋のドアを開くと同時に「ヨッ社長!一服してっていい?」と元気に挨拶していた。 会社へ訪れて「ヨッ社長!一服してっていい?」と元気に挨拶する文化に触れたことがなかったので、新鮮だった。

多分自分がそういう風な環境にずっといることはできないんだろうとは思うけど「余裕がある」「生活が楽しそう」というのは豊かだな、良いなと思った。

Assets/ に DLL をほうりこむ時

$ xbuild X/X.csproj

とかってやって作った DLL は、Unity のエディタに D&D して取り込んだ場合は問題なく使えるが、 Unity とは関係のないところでコピーすると Unhandled Exception: System.Reflection.ReflectionTypeLoadException: The classes in the module cannot be loaded. と言われて使えない。

なんでそうなのかはよく分からんけど、git submodule で管理してるやつをビルドして Assets/ にコピーしてということをコマンドでやりたいのでそれはちょっと困る。

こうやって作ると良かった。

unity-dll.sh:

#!/bin/sh
UNITY=/Applications/Unity/Unity.app/Contents
$UNITY/Mono/bin/smcs -r:$UNITY/Managed/UnityEngine.dll -r:$UNITY/Managed/UnityEditor.dll -out:$1 -target:library $2
$ ./unity-dll.sh Foo.dll Foo/*.cs

Unity - マニュアル: マネージド プラグイン に書いてあったけど、ここに書いてるのとは UnityEngine.dll の場所が微妙に違う。自分のところでは Contents/Frameworks/Managed/UnityEngine.dll ではなく Contents/Managed/UnityEngine.dll にあった。

勝手に空いてるポートでやってほしい

docker-compose:

version: '2'
services:
  web:
    build: .
    ports:
      - $PORT:3000

こういうのがあった時に、どこが空いてるか考えて PORT=3033 docker-compose up とするのが面倒。 かといってハードコードしてしまったら被った時にさらに面倒。なので勝手に空いてるところでやってほしい。

randport:

#!/usr/bin/env ruby

require 'socket'

s = TCPServer.open(0)
port = s.addr[1]
s.close
STDOUT.puts "randport: PORT=#{port}"
exec("PORT=#{port} #{ARGV.join(' ')}")

これで楽

$ randport docker-compose up
randport: PORT=53450
...

追記

とても間抜けなことをやっていたことに気付き、赤面。

とても楽。とても良い

3DCG手習い

学習/馴れるコストが異様に高いというイメージを持っていたけど意外とそんなことはなくて、1日やってみたらちょっと満足感を得られる程度には形になった。思ったようなものを作れるまでかなり遠いもののこの調子だと適度に続けられそうな気がする。


sculptris で粘土をこねる。

f:id:ToQoz:20160817222146p:plain

blender に import してボーンを設定する。色んなポーズが取れるようになる。

f:id:ToQoz:20160817222154p:plain

f:id:ToQoz:20160817222201p:plain

動かしてみた。

https://i.gyazo.com/50ed20cf043c5fc436692f8e8768a28a.gif

見たもの

操作の解説が分かりやすくて、自分の環境で一部のショートカットが効かなくて困ったこと以外は結構スムーズにいった。

気軽な playground

gopwt 用の play.golang.org みたいなのを作りたいなと思っていた。だからといって、入力されたコードをサーバーで実行しても大丈夫なような環境を作るのも面倒ということで、gopwt で変換して、それを、gopherjs で javascript にして、ブラウザで実行してもらえば良いというとても気軽な案を思いついたら結構うまくいっている。そのうち play.gopwt.org 的なところで動かす予定。

テストコードが対象になるので、go test がやっているような main.main を作ってやらないといけないんだけど、それは適当に手を抜きつつこんなテンプレートで作ってる。

testmain.go:

package main

import (
    "testing"
)

var benchmarks = []testing.InternalBenchmark{}
var examples = []testing.InternalExample{}
var tests = []testing.InternalTest{
{{range .}}
    {
        "{{.}}",
        {{.}},
    },
{{end}}
}

func main() {
    match := func(pat string, str string) (bool, error) {
        return true, nil
    }
    testing.Main(match, tests, benchmarks, examples)
}`

gopwt につけようと思っている expected と got の diff

github.com/sergi/go-diff/diffmatchpatch を使う。

なんか良さげな基準で文字単位か行単位かを切り替えたい。power assert は何も考えずに assert 書いとけばいい感じの出力が得られるというのが嬉しいところなので、オプションはつけたくないということで考えた。

このような用途に限って言うと、すごく似てるけどちょっと違ってどこが違うのが見つけるのが辛い時だけに文字単位の diff があったらよくて、どう見ても違うみたいなケースでは必要ないだろうという予想があって、まず文字単位で diff をとって、複数行にまたがる diff があったら行ごとの diff をとり直すということにしようかなと思った。

あと副作用的に、複数行にまたがらなければ [- -] {+ +} みたいなマークなしで色だけで ins/del 表現してもそんな分かりにくくなくて良さそうだった。

文字単位で嬉しい

word-diff:

        foo(x("a[-'-]{+"+})
bar

line-diff:

-       foo(x("a')
+       foo(x("a")
bar

行単位でいい

word-diff:

        fo{+x
        bo+}o
bar
ba[-r2-]{+z+}

line-diff

-       foo
+       fox
+       boo
 bar
-bar2
+baz