ISUCON8予選に参加した
ISUCON8の予選にokeigoさん、mayokoさんとチーム「しょラーさんのおかげ」として参加していた。 最終的な得点は8000点くらいで予選落ちだった。悔しい。
チームメイトの参加記
予選まで
とか言っていたらISUCON出てみたいけどその分野について全く知らない
— a_kawashiro (@a_kawashiro) July 15, 2018
としょらーさんの穴を埋める形で誘ってもらい、参加することになった。ご検討ください https://t.co/fOPEOwDecN
— しょラー (@shora_kujira16) July 15, 2018
ISUCONの参加が決定してからは手元でISUCON7の予選、ISUCON6の予選を立てて練習していた。 N+1クエリ問題について初めて知った。 あとsystemd、MySQL、nginx、pythonのプロファイラの設定も初めてでいろいろ勉強になった。
予選の一週間前にokeigoさんがISHOCON2をセットアップしてくれて、そのサーバを使って3人で練習した。 3人とも離れたところに住んでいるので、Discordで音声通話、Slackでチャット、GitHubでコードを共有することになった。 言語は3人とも読めるPythonを使うことにした。
予選当日
朝
6時ぐらいに目が覚めた。いつもより6時間くらい早起き。
10時〜
予選スタート。 設定ファイルやpythonのアプリの実装をデプロイするスクリプトを書いた。 h2oをnginxに切り替えるのはokeigoさんがやってくれた。 この間にmayokoさんがアプリを読んでいた。
11時〜
とりあえずベンチマークを取った。
/favicon.ico
が異常に重かったのだが、okeigoさんが設定のtypoを見つけて直したら解消した。
13時〜
僕はapp.py
の中でsheetをランダムに並び替えているところを小手先の小技で高速化しようとしていた。
が、あまり早くならなかった。
mayokoさんとokeigoさんがapp.py
のget_event
の改善をやっていた。
14時〜
mayokoさんの実装が終わった。 マージしてベンチを取ったら5000点ぐらい出て、学生3位になったので正直勝ったと思った。
15時〜
okeigoさんがredisを使ってキャッシュを実装していた。 venv環境にどうやってredisをインストールするのかがわからずに悪戦苦闘した。 結局バイナリをコピーして対応したらしい。SUGOI
16時〜
キャッシュの実装が動いたので、そろそろ3台構成にしましょうか、という話になった。 が、ポートの開放とかでいろいろ詰まった。 僕はあんまり何もできなかった。
17時〜
17:30ぐらいに3台構成が動いた。 あとは何度がベンチマークをとって終了。 最終的には7900点ぐらいでたはず。
18時〜
お祈りした。
— a_kawashiro (@a_kawashiro) September 16, 2018
19時ごろ
結果が出た。予選を通過することはできなかった。
燃え尽きたので起き上がれない
— a_kawashiro (@a_kawashiro) September 16, 2018
反省
- okeigoさんの負担が重すぎた。もうちょっとサーバの設定とか練習しとけばよかった。
- 役割分担がきちんと決まっていなかった。アプリ2人、インフラ1人というのがバランスが良さそうだった。
- 複数台構成におけるサーバ間の通信についてわかっていなかった。ISUCONのような特殊な環境ではファイヤーウォールを切って、全サービスが
0.0.0.0:port
をリッスンしてしまえば良いと思った。 - サービスとして登録するpythonスクリプトに新しくライブラリ(
redis
)を追加する処理についてわかっていなかった。 - プロファイルをちゃんと見ていなかった。ログの可視化スクリプトを作成しておくべきだった。
まとめ
チカラが足りなかった。悔しい。
Vimで初めてコマンドを定義してみた
書かないと忘れちゃうから…
これだけ
:command! MT :MerlinTypeOf :MT
Vimをコンパイルしてインストールする
使っているプラグインの関係で+python,+lua,+perlなVimが必要になりました。
しかし、Ubuntuのパッケージで条件を満たすものが見当たりません。
そこで自分で一からコンパイルしてインストールしました。
僕は自分でコンパイルしたソフトウェアを$HOME/.local/以下にインストールしているので、
--prefix=$HOME/.localとしています。
必要に応じて適当に変更してください。
git clone https://github.com/vim/vim cd vim ./configure --prefix=$HOME/.local/ --with-features=huge --enable-multibyte --enable-gpm --enable-cscope --enable-fail-if-missing --enable-perlinterp --enable-python3interp --enable-rubyinterp make install
あとは$HOME/.local/bin/vimにパスを通すだけです。
nvimに移行したほうがいいのかなぁ。
githubのバックアップを取る
年末も近くなってきたのでgithubが落ちた時に備えてリポジトリのバックアップを取ることにしました。
以下のスクリプトを実行するとgithub以下にすべてのリポジトリがバックアップされます。
YOUR_ACCESS_TOKENはSign in to GitHub · GitHubで作れます。
Select scopesのところでreposの欄をすべてチェックしてトークンを作成してください。
#! /bin/bash token=YOUR_ACCESS_TOKEN curl "https://api.github.com/user/repos?access_token=${token}&per_page=100&affiliation=owner" 2>/dev/null -o tmp repos=`cat tmp | grep ssh_url | cut -d" " -f 6 | sed -e 's/"//g' | sed -e 's/,//g'` mkdir github cd github for r in ${repos}; do name=`echo ${r} | sed -e "s:git.*/::" -e "s:[.]git::"` git clone ${r} cd ${name} for branch in `git branch -a | grep remotes | grep -v HEAD | grep -v master `; do git branch --track ${branch#remotes/origin/} $branch done cd .. done rm tmp
これでgithubが落ちても安心ですね。
Freeモナドについて今更勉強してみた
5年ほど前、Freeモナドという言葉がHaskell界隈でよく見られました。
僕のフォローしている人たちが次々に解説記事をアップしていたように思います。
5年たった今になって、Freeモナドについて興味が湧いてきたので記事を書いてみることにしました。
Freeモナドの定義に行く前にFree型について説明します。
-- The kind of f is * -> * like Maybe, []. -- The kind of a is * like Int, Char, [Char]. data Free f a = Pure a | Free (f (Free f a))
ここでfはカインド * -> * を持つ型コンストラクタであり、あとで述べるようにFunctorのインスタンスです。
aはカインド * のただの型です。
fが[]、aがIntの場合について調べてみましょう。
free1 :: Free [] Int free1 = Pure 10 free2 :: Free [] Int free2 = Free [Free [Free [Free []]]] free3 :: Free [] Int free3 = Free [Pure 10]
これら3つの定数関数はすべてFree [] aの型を持ちます。
直感的に言うとリストもリストのリストのリストもただの整数もすべて同じ型になっています。
これだけではFree型をどう使っていいのかわかりません。
これをモナドにしてみましょう。
-- functor is typeclass which "fmap" is defined -- (<$>) :: Functor f => (a->b) -> f a -> f b -- (<$>) = fmap instance Functor f => Functor (Free f) where fmap f (Pure a) = Pure (f a) fmap f (Free fa) = Free (fmap (fmap f) fa) instance Functor f => Monad (Free f) where return = Pure Pure a >>= k = k a Free fm >>= k = Free (fmap (>>= k) fm)
モナドになると何が良いのでしょうか?
簡単なゲームを作る例を見ながら、Freeモナドの利点を確認しましょう。
このゲームは
- 入力を受け取る
- 文字列を出力する
- 一秒待つ
の3つの要素の組み合わせで構成されています。
ゲームを表すデータ型がこれです。
data Game cont = GetInput (String -> cont) | Render String cont | Wait cont instance Functor Game where fmap f (GetInput g) = GetInput (f . g) fmap f (Render str cont) = Render str (f cont) fmap f (Wait cont) = Wait (f cont)
各コンストラクタは動作を行ったあとの続きの計算を持ちます。
例えば、GetInput fだったら入力を受け取ってfに渡すことで
続きの計算contを計算し、contを実行します。
ここでGameデータ型をFreeモナドと組み合わせて使う利点を説明します。
game1 :: Free Game () game1 = Free (Wait (Free (Render "Hello" (Pure ())))) game1' :: Free Game () game1' = (Free (Wait (Pure ()))) >> (Free (Render "Hello" (Pure ())))
一秒待って"Hello"と出力するゲームを2つの方法で記述してみました。
game1は愚直な記述、game1'は>>演算子を用いた記述です。
実はこの2つは全く同じものです。
game1'を定義にしたがって展開してみます。
(Free (Wait (Pure ()))) >> (Free (Render "Hello" (Pure ()))) ==> (Free (Wait (Pure ()))) >>= (\x -> (Free (Render "Hello" (Pure ())))) ==> Free (fmap (>>= (\x -> (Free (Render "Hello" (Pure ()))))) (Wait (Pure ()))) ==> Free (Wait ((>>= (\x -> (Free (Render "Hello" (Pure ()))))) (Pure ()))) ==> Free (Wait ((Pure ()) >>= (\x -> (Free (Render "Hello" (Pure ())))))) ==> Free (Wait (Free (Render "Hello" (Pure ()))))
同じになりましたね。
モナドの枠組みを使えばGameデータ型同士を簡単に合成できることがわかります。
input :: (String -> Free Game ()) -> Free Game () input f = Free (GetInput f) render :: String -> Free Game () render str = Free (Render str (Pure ())) wait :: Free Game () wait = Free (Wait (Pure ())) miniGame = do render "Please input your name." input (\x -> render $ "Your name is " ++ x) wait render "End"
miniGame関数ではdo記法で3種類のGameコンストラクタを合成しています。
あとはFree Game ()をIO ()に変換する、つまり実際の入出力を行う部分を書けば完成です。
runGame :: Free Game () -> IO () runGame (Pure ()) = return () runGame (Free (Wait cont)) = runGame cont runGame (Free (GetInput f)) = getLine >>= \s -> runGame (f s) runGame (Free (Render str cont)) = putStrLn str >> runGame cont main :: IO () main = runGame miniGame
コンパイルしてmain関数を実行すると名前を聞いてくれます。(ソースコードはこの記事の末尾にあります。)
結局Freeモナドを使うとどんな利点があるのでしょうか。
この記事の例ではゲームの構造と入出力が分離できています。
つまり、テストがしやすいということが言えます。
またゲームの構造だけ作っておいてあとで入出力の部分を差し替えたりするのも簡単です。
Freeモナドを使った2Dゲーム用のライブラリというのもあります。
参考
Freeモナドって何なのさっ!? - capriccioso String Creating(Object something){ return My.Expression(something); }
そろそろFreeモナドに関して一言いっとくか - モナドとわたしとコモナド
以下ソースコード
続きを読む型レベル自然数を使って型付き行列を作ってみた
#動機
研究で行列計算ライブラリを使っているのですが、numpyはなぜか異なる型の行列同士を足せてしまいます。
このせいでデバッグがめちゃくちゃ大変でした。
こんな単純な間違いはコンパイル時に弾いてくれればいいのにと思ったので、違った型の行列は足せないようなちょっとしたライブラリを作ってみました。
※行列を安全に計算したい人のためには
↑のライブラリが既に存在しました。不勉強ですみません。この記事は「型レベル自然数ってこんなに簡単に(僕でも)使えるんだ」、程度に読んでください。
型レベル変数
最近のGHCでは、自然数をパラメーターとするデータ型を定義できる。固定長リストの長さを型に持たせるとか、行列の大きさを型に持たせるとか、そういうことができる
行列の大きさの情報を型に持たせることができる。 →やってみました
仕様
- 行列の足し算・掛け算をサポートする・
- 足し算/掛け算できないものを計算しようとしたらコンパイル時にエラーにする
実装
{-# LANGUAGE DataKinds, KindSignatures #-} {-# LANGUAGE TemplateHaskell #-} module SizeFixedMatrix where import GHC.TypeLits import Data.List.Split import Data.List data SizeFixedMatrix (h :: Nat) (w :: Nat) = MakeMartix [[Integer]] deriving (Eq,Show) mat1 = MakeMartix [[1,2],[2,3]] :: (SizeFixedMatrix 2 2) mat2 = MakeMartix [[2,9],[1,3],[4,3]] :: (SizeFixedMatrix 3 2) mat3 = MakeMartix [[5,9],[3,3],[0,3]] :: (SizeFixedMatrix 3 2) add :: SizeFixedMatrix p q -> SizeFixedMatrix p q -> SizeFixedMatrix p q add (MakeMartix m1) (MakeMartix m2) = MakeMartix (map (\(a,b) -> map (\(c,d) -> c+d) (zip a b)) (zip m1 m2)) mul :: SizeFixedMatrix p q -> SizeFixedMatrix q r -> SizeFixedMatrix p r mul (MakeMartix m1) (MakeMartix m2) = MakeMartix $ splitEvery (length (head m2)) [sum $ zipWith (*) v w | v <- m1, w <- transpose m2]
…これだけです。拍子抜けするくらい簡単に作れました。もう少し凝ったものを作りたかったのですが、締切に間に合いそうにありませんでした。
一応ちょこっとだけ解説しておくと
SizeFixedMatrix p q
が縦p行横q列の行列の型になります。
不適切な型同士でaddしたりmulしたりするとコンパイルエラーになるので安心してプログラミングできます。
結果
$stack ghci (省略) *Main FiniteField Lib SizeFixedMatrix> mat1 MakeMartix [[1,2],[2,3]] *Main FiniteField Lib SizeFixedMatrix> mat2 MakeMartix [[2,9],[1,3],[4,3]] *Main FiniteField Lib SizeFixedMatrix> mat3 MakeMartix [[5,9],[3,3],[0,3]]
足し算
*Main FiniteField Lib SizeFixedMatrix> add mat1 mat2 <interactive>:4:10: error: • Couldn't match type ‘3’ with ‘2’ Expected type: SizeFixedMatrix 2 2 Actual type: SizeFixedMatrix 3 2 • In the second argument of ‘add’, namely ‘mat2’ In the expression: add mat1 mat2 In an equation for ‘it’: it = add mat1 mat2
mat1は2行2列の行列型、mat2は3行2列の行列型なので足せないと言われてしまいます
*Main FiniteField Lib SizeFixedMatrix> add mat2 mat3 MakeMartix [[7,18],[4,6],[4,6]]
掛け算
*Main FiniteField Lib SizeFixedMatrix> mul mat1 mat2 <interactive>:8:10: error: • Couldn't match type ‘3’ with ‘2’ Expected type: SizeFixedMatrix 2 2 Actual type: SizeFixedMatrix 3 2 • In the second argument of ‘mul’, namely ‘mat2’ In the expression: mul mat1 mat2 In an equation for ‘it’: it = mul mat1 mat2 *Main FiniteField Lib SizeFixedMatrix>
mat1*mat2(行列積)は存在しません
*Main FiniteField Lib SizeFixedMatrix> mul mat2 mat1 MakeMartix [[20,31],[7,11],[10,17]]
感想
型レベル変数などという難しい名前の機能をはじめて使いました。案外簡単に使えたので、他にも応用できると思います。
参考文献
hstackを教えてもらったツイート
@igrep @a_kawashiro hmatrixのNumeric.LinearAlgebra.Staticがそんな感じです。 https://t.co/e66NSsTNDr
— Masahiro Sakai (@masahiro_sakai) December 15, 2016
CTF入門記
11月の終わりからCTFをかじり始めました。この熱意がいつまでつづくのか分からないのですがとりあえずこの二週間くらいでやったことを書いてみようと思います。
動機
CTFは高校の先輩に一年ほど前から勧められていたのですが放置していました。最近寒いし何か家の中でできることないかなと考えてCTFを思い出したので初めてみました。
CTF入門します
— a_kawashiro (@a_kawashiro) 2015, 11月 27
絶望
別にPCを初めて触るわけでもないし、ksnctfのポイントの低い問題ならなんとかなるやろ…
わからなすぎてペンペン草も生えない
— a_kawashiro (@a_kawashiro) 2015, 11月 30
舐めてました。難しすぎる。というわけで梅田の丸善ジュンク堂で
をお買い上げてしまいました。その後も
POSTリクエストの中身をいじって送信するだけのことができない
— a_kawashiro (@a_kawashiro) 2015, 12月 1
SQLインジェクションって存在するんだ(無知)
— a_kawashiro (@a_kawashiro) 2015, 12月 4
などと(初心者なので)当然すぎる知識不足を露呈し続けました。
感動
FLAG取ったぜ!!
— a_kawashiro (@a_kawashiro) 2015, 11月 30
初めて自力で解いたのはksnctf - 8 Basic is secure?だったと思います。嬉しかったですね。(小並感)