コマンドラインを複数回実行して実行時間を計測するツールを書いた
なんか最近ツール系のやつを趣味で高速化したりしていて、PR投げるときに速くなったよと説明するときに手元で何度か計測して、平均値とか調べるのがめんどいなと思って、複数回実行して平均/標準偏差を表示してくれるツールを作った。
もっと定番のがあるような気がするけど、あんまり聞いたことがない。
Usage of benchcmd: -n int number of times to run (default 10) -summary only output summary result
$ benchcmd -n 10 'go version' run "go version" 15.711435ms 19.42685ms 19.967876ms 24.896109ms 19.557516ms 21.698995ms 16.499163ms 22.28795ms 23.461732ms 18.743192ms count: 10 times executed avg: 20.225081ms stdev: 2.634089ms
importするだけですべてのHTTP通信をログる
今回のisuconに対応できなかったので、作ろうと思ったけど、id:motemenさんが既に作ってた
昔見た記憶があったのだけど、すっかり記憶から忘れていた。
何msぐらいかかったかとかは出てこないので出力できるようにしてみたが、拡張方法が互換性がないのでmergeしてもらえないかもしれない。
とりあえずmergeされなくても使えるように整えておいた。
2行書きかえるだけでSQLをログに出力できるようになるgo-sql-tracerを作りました
isucon予選中に、SQLを出力したくなったのだけど、ふとgithub.com/shogo82148/go-sql-proxyを使ってみようとしてとりあえず簡単に使う方法が書いてなくて少し使い方を調べるのに苦労したので、isuconで時間のロスなく使えるようにそのwrapperを書きました。
package main import ( database/sql "github.com/go-sql-driver/mysql" # load driver before load go-sql-tracer _ "github.com/walf443/go-sql-tracer" ) func main() { db, err := sql.Open('mysql:trace', dsn) }
ポイントは、
です。
今database/sqlに登録されているドライバ一覧から、go-sql-tracerのinit内で、':trace'をつけてドライバを登録しているので使いたいドライバの後でimportする必要があります。
go-sql-proxyはカスタマイズも柔軟にでき、非常に素晴らしいライブラリですが、カスタマイズしなくてもいいのでとりあえずSQLのログみたい、というときは非常に便利かと思います。
じわじわチャットへ通知する
なんかのバッチを実行していて、それが出力するログをじわじわチャットへ通知したい。(そのバッチはそれなりに時間がかかるものとする)
何も考慮しなければ、たとえばslackであれば、
# #!/bin/bash # notify_chat.sh exec tee >(while read line; do curl -s --data '$line' $SLACK_ENDPOINT -o/dev/null; done
みたいにやれば、
$ ruby batch.rb | notify_chat.sh
で毎行slackさんへ通知してくれる。
このやり方だと、場所によってはAPIをどばっと叩いてしまって負荷をかけてしまったりするので、1秒間の出力はまとめてわたせるようにするコマンドthrottleというものを書いてみた。
$ ruby batch.rb | tee >(throttle -interval 1s "curl -s --data '%%DATA%%' $SLACK_ENDPOINT -o /dev/null")
みたいにやると、1秒間の出力はまとめてcurlコマンドへ渡してくれる。1秒間の出力は、%%DATA%%の部分が置きかえられて実行される
$ go install github.com/walf443/throttle
でインストールできる
ソースは、
ppの出力先を変えられるようにした
pp( k0kubun/pp · GitHub
)は非常にお気軽に使えてよいのだけど、デバッグ中にちょっと出力がウザいので抑えたり、また見たりしたいなー、というときに、import文をコメントアウトしたり変数を_に変えたりするのがめんどくさかった(それがGo wayなのではあるけれども)
そこで、DEBUGが有効なときに使える、Debug*みたいな関数群を追加するのを提案したところ、出力先を切り替えられるようにしてはどうか?という話になったので、実装して無事、取りこまれた。
customizable Print* functions output by walf443 · Pull Request #6 · k0kubun/pp · GitHub
import ( "github.com/k0kubun/pp" "io/ioutil" "os" ) func init() { if os.Getenv("DEBUG") == "" { pp.SetDefaultOutput(ioutil.Discard) } }
こんな感じにしておくと、pp.PrintしているところがDEBUG環境変数が有効じゃないと出力されなくなるので、コードを変更しなくてもppの出力を抑制できるようになる。
自分は、今書いているパーサーで、実際に使われているデータとかをparseしてASTをdumpするやつ(コミットしていない)をgo testで走るようにしておいてそれをたまにppでdumpしたりしている。便利。
ppは、さらに欲をいえば、
import ( "github.com/pp/pp" )
みたいにpp organizerでホストされているようになれば、pp/ppで何も考えずimportできるようになって個人的にはとてもうれしい。githubはアクティビティが低いユーザーで良い名前を抑えていた場合、交渉して取れるようになっていると聞いたことがあるような気がするが、この場合はどうだろうか。
追記
import "gopkg.in/pp.v1"
できるようになったらしい。すばらしい。
カジュアルに実行時間を計測する
isuconの予選で、カジュアルにここどれぐらい時間がかかっているか計測したい、ということで作った。
pprofは(使い方間違っているだけなのかもしれないけど)CPU時間での計測なので、io待ちとか、channel待ちとかの時間は出てこないので、web applicationのチューニングにはあまり適さないのかなという気がする。
https://github.com/walf443/stopwatch
やりたいこととしては、
- 計測コードを簡単に無効にできる
- ここから、ここまで、という指定以外に前回の実行からどれぐらいたったのか? というのも自動でとりたい
ということを達成したかった。
package main import ( "flag" "github.com/walf443/stopwatch" "time" ) func main() { flag.Parse() stopwatch.Reset("start request") time.Sleep(100 * time.Millisecond) stopwatch.Watch("after something") time.Sleep(100 * time.Millisecond) stopwatch.Watch("after something 2") }
こんな感じで、HTTPのサーバーであれば、リクエストの関数の冒頭で、Resetを呼びだし、あとは、計測したいタイミングで、Watchを呼びだしていけばよい
デフォルトでは無効になっているので、計測するには、--stopwatchフラグをつけながら起動すると、
$ go run example.go --stopwatch stopwatch|2014/10/07 09:27:33 [ 0][ 0] start request at /Users/yoshimin/project/go/src/github.com/walf443/stopwatch/example/example.go:12 stopwatch|2014/10/07 09:27:33 [ 100.892037ms][ 100.892037ms] after something at /Users/yoshimin/project/go/src/github.com/walf443/stopwatch/example/example.go:14 stopwatch|2014/10/07 09:27:33 [ 201.129537ms][ 100.2375ms] after something 2 at /Users/yoshimin/project/go/src/github.com/walf443/stopwatch/example/example.go:16
こんな感じで標準出力にログが出てきて、Resetを呼びだしてからの時間と、前回Watchをしてからの時間がわかる。
グローバル変数で実現しているので、関数間でも、別のファイル間でもカジュアルにstopwatch.Watchとか呼べばよい。
もちろんその反面、HTTPサーバーのハンドラに仕込んでいるときに、同時にリクエストが来て、Resetが呼ばれたりすると、カウンタがおかしくなるが、手動で動作させつつ、大まかな時間を調べるのが目的なので、使い勝手のカジュアルさを重視した。
使い終わったら、stopwatchが書いてある行を削除すれば、だいたい消えてくれると思われる。
matsuuさんに、Practical Goで、Nitroの話を聞いたけど、何って名前だったか覚えてなかったので、適当に作った。あっちの方がライブラリとしてはよっぽどちゃんとしている。