wassup?

新ブログ→memo.wass80.xyz

Rubyのtrace命令の話を確かめる

過去のblogから移動しました 元公開日時 2017-12-31 06:10:19

こんばんは。
この記事はKMCアドベントカレンダー23日目の記事です。大遅刻です。

前日の記事はprimeさんの
ビット演算マニアのためのAVX512入門 【KMCアドベントカレンダー 22日目】 - prime's diaryです

明日の記事はtetsutalowさんの
3つの事件で振り返る「何をやったらウイルス罪で捕まるか2017」 - Tetsu=TaLowの雑記(はてブロ版)です

本題

Ruby 2.5 の改善を自慢したい

インターンの講義でお世話になった笹田さんの上の記事を読みました。
その記述を確かめながら小ネタにしようと思います。

次の素朴なコードのベンチマークを見てください。

require 'benchmark'
Benchmark.bmbm 10 do |r|
  r.report "Normal" do
    1000000.times {
      a = 0
      b = 1
      100.times {
        c = a + b
        a = b
        b = c
      }
    }
  end
  r.report "Boost" do
  boost %q(
    1000000.times {
      a = 0
      b = 1
      100.times {
        c = a + b
        a = b
        b = c
      }
    }
  )
  end
  //省略
end
                 user     system      total        real
Normal       9.510000   0.130000   9.640000 ( 10.713862)
Boost        8.590000   0.090000   8.680000 (  9.021931)

Boostのほうが速いようです。
boost %q()で囲むだけでちょっと速くなっているようにみえます。

ねたばらし

boostの実装は以下のとおりです。

def boost src
  eval src.gsub("\n", ";")
end

引数の文字列の改行全てをセミコロンに置き換えて実行するだけです。

すなわち次のコードより

a = 1 + 2
b = 3 + a

このコードの方がちょっと速くなるということです

a = 1 + 2;b = 3 + a

なんでだろう?

結論としては, trace命令が減ったからです。

RubyVM

YARV

VMコードを見るには

  puts RubyVM::InstructionSequence.compile("1+1").disasm

のようにすれば"1+1"のVM命令が読めます。

== disasm: #<ISeq:<compiled>@<compiled>>================================
0000 trace            1                                               (   1)
0002 putobject_OP_INT2FIX_O_1_C_
0003 putobject_OP_INT2FIX_O_1_C_
0004 opt_plus         <callinfo!mid:+, argc:1, ARGS_SIMPLE>, <callcache>
0007 leave

細かいところは説明しません。行番号と命令が1行ずつかかれています。
雰囲気で1と1をスタックに積んで足し合わせている様子を感じ取ってください。

rubyのコードは実行するときに, このようにVM命令を一度挟んでいます。

命令の説明は
insns.def
にあるので興味があれば読んでください。

trace命令

笹田さんの記事によると, trace命令はRubyコードの各行に挟まれるようです。

実際, ベンチマークで使ったコードのVM命令を見るとtrace命令はたくさんあります。

1000000.times {
  a = 0
  b = 1
  100.times {
    c = a + b
    a = b
    b = c
  }
}
== disasm: #<ISeq:<compiled>@<compiled>>================================
== catch table
| catch type: break  st: 0002 ed: 0008 sp: 0000 cont: 0008
|------------------------------------------------------------------------
0000 trace            1                                               (   1)
0002 putobject        1000000
0004 send             <callinfo!mid:times, argc:0>, <callcache>, block in <compiled>
0008 leave
== disasm: #<ISeq:block in <compiled>@<compiled>>=======================
== catch table
| catch type: break  st: 0014 ed: 0020 sp: 0000 cont: 0020
| catch type: redo   st: 0002 ed: 0020 sp: 0000 cont: 0002
| catch type: next   st: 0002 ed: 0020 sp: 0000 cont: 0020
|------------------------------------------------------------------------
local table (size: 2, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])
[ 2] a          [ 1] b
0000 trace            256                                             (   1)
0002 trace            1                                               (   2)
0004 putobject_OP_INT2FIX_O_0_C_
0005 setlocal_OP__WC__0 4
0007 trace            1                                               (   3)
0009 putobject_OP_INT2FIX_O_1_C_
0010 setlocal_OP__WC__0 3
0012 trace            1                                               (   4)
0014 putobject        100
0016 send             <callinfo!mid:times, argc:0>, <callcache>, block (2 levels) in <compiled>
0020 trace            512                                             (   9)
0022 leave                                                            (   4)
== disasm: #<ISeq:block (2 levels) in <compiled>@<compiled>>============
== catch table
| catch type: redo   st: 0002 ed: 0026 sp: 0000 cont: 0002
| catch type: next   st: 0002 ed: 0026 sp: 0000 cont: 0026
|------------------------------------------------------------------------
local table (size: 1, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])
[ 1] c
0000 trace            256                                             (   4)
0002 trace            1                                               (   5)
0004 getlocal_OP__WC__1 4
0006 getlocal_OP__WC__1 3
0008 opt_plus         <callinfo!mid:+, argc:1, ARGS_SIMPLE>, <callcache>
0011 setlocal_OP__WC__0 3
0013 trace            1                                               (   6)
0015 getlocal_OP__WC__1 3
0017 setlocal_OP__WC__1 4
0019 trace            1                                               (   7)
0021 getlocal_OP__WC__0 3
0023 dup
0024 setlocal_OP__WC__1 3
0026 trace            512                                             (   8)
0028 leave

trace 1 というのがコードの行ごとに挟まるVM命令です。
この命令が7回も現れているのがわかります。

これに対して, コードの改行を削除して以下のように(広義の)ワンライナーにした場合, VM命令はこうなります。

1000000.times {;  a = 0;  b = 1;  100.times {;    c = a + b;    a = b;   b = c;  };};
== disasm: #<ISeq:<compiled>@<compiled>>================================
== catch table
| catch type: break  st: 0002 ed: 0008 sp: 0000 cont: 0008
|------------------------------------------------------------------------
0000 trace            1                                               (   1)
0002 putobject        1000000
0004 send             <callinfo!mid:times, argc:0>, <callcache>, block in <compiled>
0008 leave
== disasm: #<ISeq:block in <compiled>@<compiled>>=======================
== catch table
| catch type: break  st: 0010 ed: 0016 sp: 0000 cont: 0016
| catch type: redo   st: 0002 ed: 0016 sp: 0000 cont: 0002
| catch type: next   st: 0002 ed: 0016 sp: 0000 cont: 0016
|------------------------------------------------------------------------
local table (size: 2, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])
[ 2] a          [ 1] b
0000 trace            256                                             (   1)
0002 trace            1
0004 putobject_OP_INT2FIX_O_0_C_
0005 setlocal_OP__WC__0 4
0007 putobject_OP_INT2FIX_O_1_C_
0008 setlocal_OP__WC__0 3
0010 putobject        100
0012 send             <callinfo!mid:times, argc:0>, <callcache>, block (2 levels) in <compiled>
0016 trace            512
0018 leave
== disasm: #<ISeq:block (2 levels) in <compiled>@<compiled>>============
== catch table
| catch type: redo   st: 0002 ed: 0022 sp: 0000 cont: 0002
| catch type: next   st: 0002 ed: 0022 sp: 0000 cont: 0022
|------------------------------------------------------------------------
local table (size: 1, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])
[ 1] c
0000 trace            256                                             (   1)
0002 trace            1
0004 getlocal_OP__WC__1 4
0006 getlocal_OP__WC__1 3
0008 opt_plus         <callinfo!mid:+, argc:1, ARGS_SIMPLE>, <callcache>
0011 setlocal_OP__WC__0 3
0013 getlocal_OP__WC__1 3
0015 setlocal_OP__WC__1 4
0017 getlocal_OP__WC__0 3
0019 dup
0020 setlocal_OP__WC__1 3
0022 trace            512
0024 leave

わかりにくいと思いますが, 行trace命令は7つから2つになり, 5つも減っています。
このため, 実行がすこし速くなったわけです。

trace命令の定義

trace命令の定義はinsns.defにあります。

nopほど単純ではない処理が行われているのがわかります。
記事に説明されていたとおり, TracePointという機能で使われているようです。

もうちょっと検証

最初のベンチマークの時にもう少し検証をしています。
boostの定義とほとんど同じですが,
セミコロンではなく元の改行のままにするnonboostを定義しました。

def nonboost src
  eval src.gsub("\n", "\n")
end

boostnonboostの違いは";""\n"だけです。
このベンチマークは以下のようになりました。

Rehearsal ----------------------------------------------
Normal      10.420000   0.210000  10.630000 ( 13.289735)
Boost       10.280000   0.190000  10.470000 ( 12.934161)
NonBoost    10.750000   0.200000  10.950000 ( 13.520492)
------------------------------------ total: 32.050000sec

                 user     system      total        real
Normal      10.260000   0.160000  10.420000 ( 11.629897)
Boost        8.970000   0.120000   9.090000 (  9.784410)
NonBoost     9.820000   0.140000   9.960000 ( 11.012133)

何度か実行してみると, Boost <<< NonBoost < Normal の順に遅い感じがしました。
明らかにNonBoostは余計な処理を行っているのにも関わらず, 遅くならないのは不思議です。

ruby2.5の場合

このベンチマークは以下のruby2.4で実行しましたが,

ruby 2.4.3p205 (2017-12-14 revision 61247) [x86_64-darwin16]

次のruby2.5では続く結果のように, どれもほとんど差がありません。

ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin16]
Rehearsal ----------------------------------------------
Normal      12.007801   0.255304  12.263105 ( 15.818664)
Boost       12.588323   0.237480  12.825803 ( 15.782480)
NonBoost    12.292849   0.235013  12.527862 ( 14.568634)
------------------------------------ total: 37.616770sec

                 user     system      total        real
Normal      11.921363   0.238929  12.160292 ( 13.990406)
Boost       11.551764   0.235187  11.786951 ( 13.314673)
NonBoost    11.725629   0.180515  11.906144 ( 13.135241)

これは最初の記事で紹介されている通り, trace命令が抑制される最適化が施されたためです。

実際の命令を見てみると, 1つもtrace命令が存在しません。

== disasm: #<ISeq:<compiled>@<compiled>:1 (1,0)-(9,5)>==================
== catch table
| catch type: break  st: 0000 ed: 0006 sp: 0000 cont: 0006
== disasm: #<ISeq:block in <compiled>@<compiled>:1 (1,14)-(9,5)>========
== catch table
| catch type: break  st: 0007 ed: 0013 sp: 0000 cont: 0013
== disasm: #<ISeq:block (2 levels) in <compiled>@<compiled>:4 (4,16)-(8,7)>
== catch table
| catch type: redo   st: 0001 ed: 0019 sp: 0000 cont: 0001
| catch type: next   st: 0001 ed: 0019 sp: 0000 cont: 0019
|------------------------------------------------------------------------
local table (size: 1, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])
[ 1] c
0000 nop                                                              (   4)[Bc]
0001 getlocal_OP__WC__1 a                                             (   5)[Li]
0003 getlocal_OP__WC__1 b
0005 opt_plus         <callinfo!mid:+, argc:1, ARGS_SIMPLE>, <callcache>
0008 setlocal_OP__WC__0 c
0010 getlocal_OP__WC__1 b                                             (   6)[Li]
0012 setlocal_OP__WC__1 a
0014 getlocal_OP__WC__0 c                                             (   7)[Li]
0016 dup
0017 setlocal_OP__WC__1 b
0019 leave                                                            (   8)[Br]
| catch type: redo   st: 0001 ed: 0013 sp: 0000 cont: 0001
| catch type: next   st: 0001 ed: 0013 sp: 0000 cont: 0013
|------------------------------------------------------------------------
local table (size: 2, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])
[ 2] a          [ 1] b
0000 nop                                                              (   1)[Bc]
0001 putobject_OP_INT2FIX_O_0_C_                                      (   2)[Li]
0002 setlocal_OP__WC__0 a
0004 putobject_OP_INT2FIX_O_1_C_                                      (   3)[Li]
0005 setlocal_OP__WC__0 b
0007 putobject        100                                             (   4)[Li]
0009 send             <callinfo!mid:times, argc:0>, <callcache>, block (2 levels) in <compiled>
0013 leave                                                            (   9)[Br]
|------------------------------------------------------------------------
0000 putobject        1000000                                         (   1)[Li]
0002 send             <callinfo!mid:times, argc:0>, <callcache>, block in <compiled>
0006 leave

これによってほぼ任意のRubyのコードが数%速くなったのだと思います。すごい。

注意

記事にあるように, Ruby2.5以前でこの最適化を施したければ,

RubyVM::InstructionSequence.compile_option = {trace_instruction: false}

を使うべきです。

Cookpadインターン参加記 または 東京観光

過去のblogから移動しました 元公開日時 2017-09-01 15:30:00

Cookpadインターン2017に参加していました。
その時の行動ログを写真とともに残しておきます。

人事さんのごはん
Cookpadには社食というものはなく, 代わりにキッチンがあります。インターン前半の講義フェイズでは, お昼ごはんは人事さんが作ってくださいました。どれも美味しかったです。

美味しいカレー
特にこのキーマカレーはとても美味しかったです。残ったカレーを講義終わってから温め直して食べました。講義中はこの事以外考えてなかった気がします。

コミケ
インターンの初週が終わると, はじめてのコミケがありました。初日だけ参加しました。KMCのブースと辺りの技術系ブースを眺め, ボカロCDを何枚か買いあさりました。

ボドゲ会
とあるボドゲ会にお邪魔することができました。東京に来てしばらくボドゲができていませんでした。そのため, 11時間連続でボドゲしたこの日はとても満足しました。

大会
翌日, KMCの仲間とともに謎の大会に参加しました。ボドゲの筆記テストあり, キャプテンリノを高く積み上げ, よくあるジレンマ問題をやり, 交渉のゲームでは無になりました。こういう気楽な遊びは神経を使わなくて本当に楽しいです。

スカイツリー
スカイツリーを下から眺めました。京都タワーとそれほど変わらない気もしました。しかし, 京都駅に帰ったところ京都タワーはめちゃくちゃ低いことに気づきました。

チキンカツ
一番恋しかったのはハイライトのチキンカツですね。代わりになるものを探して一番近かったのは浜勝のチキンカツでした。

唐揚げ
Cookpadのキッチンで唐揚げを作りました。唐揚げはKMCでも作ったことがあるので余裕ですね。オフィスの冷蔵庫にはお世話になりました。

ボス
最終週の休みに上野のクレーンゲームでラッキービーストを手に入れました。その流れでけものフレンズ展にも足を運びました。かばんにボスを入れているとかばんちゃんの気持ちになれます。このボスは実物大でめちゃくちゃでかいので, 僕のかばんのメインスペースを専有しました。最終的にその状態で京都に帰ることになりました。

恵比寿
恵比寿のビルの最上階から見える恵比寿駅です。複々線を眺めるのは楽しいです。後半のお仕事フェイズでは, 外の風景に癒やされていました。

肉
インターン最終日の肉です。おいしい。

インターンの内容を何も書いてない気がしてきました。

やりたいことを見つけられたらいいなと言う気持ちでインターンに参加していました。インターンでやった事自体は自分にとってそれほど大きな価値はなかった気がします。結局やりたいことを見つけられれば, すぐに手に入るものばかりだった気がします。

やりたいことって見つかるものなんですかね…。
とりあえずもっと手を動かさないと何も始まらないと思うので, 夏休みの後半はどうにかします。どうにかしたい。
2年ぶりにエンジニアのコミュニティーに属せて, やる気がでてきたつもりになったので頑張ります。

バイトをやめた

過去のblogから移動しました 元公開日時 2017-08-06 12:37:34

いわゆる退職エントリです。

大学でRとbashで遺伝子データの解析を1年超やってました。
辞めた理由は1ヶ月のインターンに行くためです。
インターンの後も学部3,4回生はそれなりに忙しいのもあります。
学部生は大学で働いても専門性が加味されず,
あんまりお金にならない。

遺伝子学の分野は実験(WET)と解析(DRY)の混合になります。WETとDRY両方に精通している人間は多くないようです。僕はもちろんDRY。実験室は一度見たきり。

マウスやヒトのリファレンスゲノムってかんたんにダウンロードできるんです。ヒトの実際のAやらGやらCやらTやらを眺めるのは面白かったです。自分もこれとほとんど同じなの不思議に感じます。あと, 誰の遺伝子なんだろう。リファレンスゲノムは時々アップデートされるので, 誰か1人のというわけではなさそう。

辛かったのはR言語。Rを使う必要が何故あるのかというと, 生物系のRのライブラリリポジトリがあり, そこのライブラリにはたいてい論文がついています。代わりが効きにくいのです。

Rに触れた最初はかなり苦痛でした。言語仕様と標準ライブラリはもれなくクソです。tidyverseライブラリ群にはかなり救われました。tidyverseとforeach使ってればRはマスターしたと言っていいと思います。(あといくつかの統計的モデルのライブラリが必要)

tidyverseを使う中でRはメタプログラミング言語という事に気づきました。Rの文法は流行らなかったと言われるLispのM式なのです。なので, ifも関数です。ということはRは数少ない遅延評価言語なのです。

実際に一番苦労させられたのはコマンドラインで走る雑多なDRYツール群です。Bashで長大な実験パイプラインを作るのに苦労しました。前任者の悲惨なコードを目の当たりにしていたので, かなり気を使いました。DRYの実験でgitで管理してる人間は存在するのだろうかみたいな世界観です。大変気を張ります。

雑多なツール群でめんどくさいのは, コマンドラインの引数のパーサが色とりどりすぎる点です。ハイフンが1個だったり2個だったり。引数の数が5を超えると覚えてられずにすぐに忘れます。必須なオプションって矛盾してると思うんですがどうなんでしょう。コマンドラインオプションを間違えると1,2日走らせていた実験が失敗するのも悲しいですね。シェルスクリプトの強いLinterがほしい。

色々愚痴りましたが, 別の世界観を垣間見れたのは悪いことでなかったと思います。明日から普通の情報系のコミュニティーに戻ります。インターン頑張ります。

欲しいものリストをおいておきます
http://amzn.asia/8P20UKl

ICPC国内予選2017参加記

過去のblogから移動しました 元公開日時 2017-07-19 17:07:17

こんにちは。3回生のwassです。
ICPCに参加したのでそのログを残しておきます。

僕は競プロから1年以上退いて,
活動としては部内のプロコンに月に1回程度参加するぐらいでした。
そのため実装はチームメイトに任せることにしました。

A: やってもらった。

B: やってもらった。焦って誤読しあってしまったのは良くなかった。みんなで同じ問題読む大事さを忘れていた。

C: やるだけ。やってもらった。

D: 困った。全探索解法部分はわかるが, bitDP部分でつまずく。半嘘解法を思いつき実装しているチームメイトを眺めた。オーダーがnだけ余計にかかる解法のため実行に時間がかかっていたらしい。

E: Dを実行しながらEを僕が実装することになった。構文木の全列挙で間に合うことがわかる。構文木のデータ構造を作らずに探索する方法は気づかなかった。構文木を素直に全列挙し, 恒等式判定をする実装をした。実行がコンテスト終了に間に合わなかった。あとで確認すると実装は合ってたっぽい。Dではついてた-O3を忘れていたのは事故。

G: 右手法で探索をすれば行けるやろという気分ではいた。だけ。フローは賢いなあ。。

Dは奇跡的に間に合ったので結果は4完。4完内ではビリ近くの55位。くやしい。

競プロ楽しいですね。活動を再燃させる。

悪いものを食べて判断力が鈍っているときに,
楽しそうな提案をすると乗ってくれることがわかるブログです→
http://kyp.hatenadiary.com/entry/2017/07/19/024104

部室に温度センサーとかつけて監視する

(過去のblogから移動しました 元公開日時 2016-12-22 05:45:44)

こんにちは, KMC2回生のwass80です。

この記事はKMC Advent Calendar 2016 21日目の記事です.
昨日の記事はtronくんの「Neutron 買ってみたのは いいけれど……」でした。記事タイトルが575ですね。
明日の記事はbase64くんの「いい感じのメドレーを自動生成したい」です。自分がもやりたかったことやられたので, 後でいい感じコミットぜったいしたる。

今回はRaspberryPi3 ModelBを買ったので, 使って部室の監視をしたいと思います。

概略図

いい感じ図

Raspberryにつながった温度センサーの値をfluentdでinfluxDBに送りつける。
grafanaでグラフを表示。

用意するもの

RasberryPi3はArduinoと違って, オス-メスのジャンプワイヤーが必要になるので注意しましょう。買い忘れました。

RasberryPi3

センサーをつなげて値を読み取りましょう。

セットアップ

Raspberry Pi 3を買ってMacを使ってWiFi接続とSSHの接続するまで

SDカードにRaspbianを焼いてRasberryPi3に差し込みます。
USBで電源を供給すれば起動します。HDMIで画面を見ます。
初期パスワードはuser:pi/pass:raspberryです。速やかに変更しましょう。
sshがデフォルトで無効になっているので有効化する必要があります。
Wifiでつながると便利なのでその設定もします。

温度センサー

第39回「ラズベリーパイで温度・湿度・気圧をまとめて取得!AE-BME280でIC2通信」

このセンサーにははんだ付けが必要です。
I²C方式シリアル通信をします。

上の記事通りに接続したら, I²Cを有効化します。
Github: SWITCHSCIENCE/BME280のコードを借りて(少し改変して)データを表示してみます。

t, p, h = readData()
print("気温:%f\t大気圧:%f\t湿度:%f" % (t, p, h))
pi@raspberrypi:~ $ python bme280/bme280.py
気温:19.729584     大気圧:1005.779496   湿度:56.260745

動いてそうです。

このデータを10秒おきに次のfluentdに送りつけましょう。

fluent/fluent-logger-python

from fluent import sender
import time

logger = sender.FluentSender('raspi', host='sharp')

if __name__ == '__main__':
        while True:
                t, p, h = readData()
                print("気温:%f\t大気圧:%f\t湿度:%f" % (t, p, h))
                logger.emit('climate', {'temperature': t, 'pressure': p, 'humidity': h})
                time.sleep(10)

systemd用のunitファイルを書きましょう。

#/etc/systemd/system/bme280.service
[Unit]
Description = bme280 climate sensor

[Service]
ExecStart = /home/pi/bme280/bme280.py
Restart = always
Type = simple

[Install]
WantedBy = multi-user.targe
$ sudo systemctl enable bme280
$ sudo systemctl start bme280

fluentd

ログの受け渡しをするサービス。
fluentdは Input → Filter → Output の経路でJSONのログ(event)を流します。

例えば, 以下のことができます。

  • あるログファイルの書き込みを感知して(Input)
  • それがErrorのログならば(Filter)
  • Slackへ通知する(Output)

今回は以下の構成になります。

  • TCPでログを受け取る(Input)
  • そのすべてを(Filterなし)
  • influxDBに送りつける(Output)

InputとFilterとOutputを結びつけるのは, ログに紐づくタグです。
Inputでログにタグを付け, 対応するFilter, Outputが動きます。

fluentdはすでに部室で動いていたので間借りします。

fluentdはデフォルトでTCPで受け取る以下のForward Inputが動いています。

#不要なコード
<source>
  type forward
  port 24224
  bind 0.0.0.0
</source>

RaspberryPIからraspi.climateタグをつけてfluentdに送っています。

#前述抜粋
logger = sender.FluentSender('raspi', host='sharp') #sharpはfluentdのあるサーバ名
logger.emit('climate', {'temperature': t, 'pressure': p, 'humidity': h})

なので以下の設定を追加します。

<match raspi.climate> #このタグであれば
  @type copy #次のOutputそれぞれに受け渡す
  <store> #ファイルに保存
    @type file
    path /var/log/td-agent/raspi/climate.log
  </store>
  <store> #influxdbに送りつける
    @type influxdb
    host  192.168.220.31
    port  8086
    dbname climate
    user  root
    password  root
    use_ssl false
    time_precision s
  </store>
</match>

# matchは上からマッチし, マッチしたものがあればそれ以上マッチしない。
<match raspi.**> #上にマッチしなければ, 別のファイルに保存。
  @type file
  path /var/log/td-agent/raspi/raspi.log
</match>

今回はinfluxDBに送るついでにファイルにも保存していますが, DBを真面目に運用するなら必要ないでしょう。

influxDB

field/tagキーに値を時系列で突っ込んで行くデータベース。

Key Concepts

「性別(∋男,女)」のように値の種類(=カーディナリティ)が少ないものはtagキー。
「気温=実数」のようにカーディナリティが高いものはfieldキーに指定します。

influxDBの準備にはdocker-composeを用いました。

参考: nicolargo/docker-influxdb-grafana

# docker-compose.yml
version: '2'
services:
  influxdb:
    image: influxdb:latest
    ports:
      - "8083:8083"
      - "8086:8086"
    env_file:
      - 'env.influxdb'
    volumes:
      - influxdb-storage:/var/lib/influxdb
  grafana:
    image: grafana/grafana:latest
    ports:
      - "13000:3000"
    links:
      - influxdb
    volumes:
      - grafana-storage:/var/lib/grafana
    environment:
      - GF_SERVER_ROOT_URL=%(protocol)s://example.jp/~wass80/app/grafana
      - GF_AUTH_ANONYMOUS_ENABLED=true
      - GF_AUTH_ANONYMOUS_ORG_ROLE=Admin
volumes:
  influxdb-storage:
    driver: local
  grafana-storage:
    driver: local

起動する。

$ docker-compose up -d

InfluxDB と fluentd を組み合わせを試してみた

データベースを作ればデータを受け取る準備が完了します。
influxDBのWebインターフェースはdeprecatedのようなので, 今回はCLIを用いました。

$ docker ps
CONTAINER ID        IMAGE                                COMMAND                  CREATED             STATUS              PORTS                                                                 NAMES
791734f2cf3a        grafana/grafana:latest               "/run.sh"                8 hours ago         Up 8 hours          0.0.0.0:13000->3000/tcp                                               dockerinfluxdbgrafana_grafana_1
d39bac4136e8        influxdb:latest                      "/entrypoint.sh influ"   8 hours ago         Up 8 hours          0.0.0.0:8083->8083/tcp, 0.0.0.0:8086->8086/tcp                        dockerinfluxdbgrafana_influxdb_1

$ docker exec -it d39 influx
Visit https://enterprise.influxdata.com to register for updates, InfluxDB server management, and monitoring.
Connected to http://localhost:8086 version 1.1.1
InfluxDB shell version: 1.1.1
> CREATE DATABASE climate
> SHOW databases
name: databases
name
----
_internal
climate
#fluentdの設定 前述抜粋
  <store> #influxdbに送りつける
    @type influxdb
    host  192.168.220.31 # influxDBの動くサーバ
    port  8086
    dbname climate #データベース名
    user  root
    password  root
    use_ssl false
    time_precision s
  </store>

これで{'temperature': t, 'pressure': p, 'humidity': h}というfleid:値がinfluxdbに流れます。

Grafana

influxDBの内容をめっちゃかっこよく表示してくれるいい子。

先程のdocker-composeで一緒に起動していました。
今回はBasic認証がすでにかかっているところで動かすため, Grafanaの認証を無効化しています。
リバースプロキシ用の設定を環境変数に追加しています。

Grafana: Configuration

# docker-compose.yml (前述抜粋)
  grafana:
    image: grafana/grafana:latest
    ports:
      - "13000:3000"
    links:
      - influxdb
    volumes:
      - grafana-storage:/var/lib/grafana
    environment:
      - GF_SERVER_ROOT_URL=%(protocol)s://example.jp/~wass80/app/grafana
      - GF_AUTH_ANONYMOUS_ENABLED=true
      - GF_AUTH_ANONYMOUS_ORG_ROLE=Admin

influxDBを登録しましょう。
GUIで設定できます。

あとはめっちゃいい感じGUIでグラフの設定をします。

いいですね。

できました。

凡例の色付き横線を押すと色の変更と軸の左右の変更ができます。

他にも色々センサーを買いましたが, RaspberryPIがアナログ入出力が出来ないことを知りませんでした。A-D変換を買ってきます。
明日の記事をお楽しみに。

ISUCON6本戦を復習して勉強する

(過去のblogから移動しました 元公開日時 2016-10-24 13:08:12)

ISUCON6お疲れ様でした。とても楽しかったです。

:innocent:チームは順位表で😇と表示されていました。 これは運営の方がわざわざ実装をしてくださったらしいです。 絵文字のチームが増えると 🍣と🍺問題 http://blog.kamipo.net/entry/2015/03/23/093052 が起こりうるのでやめましょう。 競技中のポータルサイトの右上に現れる"Hello, 😇"は面白かったです。 ぜひこの絵文字を結果発表で前に大きく出したかった。

:innocent:チームは与えられた実装よりスコアを下げてしまいました。 全くもって付け焼き刃が通らないという様子で作問者の思惑通りに手のひらの上で転がされた感じになりました。チームの3人の知識を合わせて0.01人の量に満たないことがわかったので, これから勉強していきたいと思います。

docker上で実行されているrackアプリにrack-lineprofileを仕込むという戦略を最初に予定していたのですが, 実行したところ一切のログが残らず混乱をしてしまいました。プロファイルを取る方法を失い, nodeでのボトルネック具合もよくわからないので, 問題点を追求できなかったです。 その場合でもCPU時間を参考にすればよかったです。

今回はチーム内での分担も行うことが出来ず, 問題に取り組む効率が最悪だったと思います。 全体の構成図を最初に書いておけば方針の決定に役立ったと思いました。 本番中はreactやらSSEやらSSLやらでかなり打ちのめされていました‥。

やることがたくさんあるとてもいい問題だったので, KMC内で本戦に出ていないチームともう一度解き直す会を行いたいです。

チームメイトutgwの様子 http://utgwkk.hateblo.jp/entry/2016/10/23/233114

ISUCON6予選学生枠通過しました

(過去のblogから移動しました 元公開日時2016-09-18 14:23:20)

びっくりしました。

:innocent:チームのチームメイトはKMCの同期の @utgwkk https://twitter.com/utgwkk くんと先輩の @yu3mars https://twitter.com/yu3mars さんでした。お疲れ様でした。

やったことは,

  1. GET /が遅くてSCOREが0

  2. とりあえずhtmlifyの無駄を関数をくくりだして呼び出しを減らす。

  3. すべてのカラムを使っていないSelect *を削減。

  4. POST /starが遅くてSCOREが0

  5. データベースを直接問い合わせる。疎結合なにそれおいしいの。

  6. 静的ファイルのキャッシュ

  7. utgwくんが調べてやってくれた。

  8. GET /keyword/が遅い

  9. 難敵。utgwくんのProfileによるとここが他の100倍ぐらいの律速

  10. 原因は動的生成される長大な正規表現gsub。

  11. 自分で実装書き換えるのは試したけど厳しかった。

  12. 頭が冷えていれば, まずテストケースを作るところからやるべき。

    • だけどそんな筋力がない。アホコラは競プロでお願いします。
  13. 終了一時間前に

pattern = pattern.select{|pat| contents.include?(pat) }

  • みたいなことを書いた以外もとの実装のままで最終SCOREの19610点
  • yu3のアイデア。さすが。

僕は初っ端にKのキートップが外れたので, vimが使えませんでした。 そのため, コード読みながらutgwくんに色々言っていました。 今回殆どの準備をutgwくんがやってくれたので彼を祭り上げたい https://www.amazon.co.jp/wishlist/3JZEEE505CI87/ref=cm_sw_r_tw_wlp_HiglL.41T49YJ です。

utgwくんのブログ → ISUCON6 に出て予選で打ちのめされてきた #isucon http://utgwkk.hateblo.jp/entry/2016/09/18/182304

キャッシュの知識がなかったので, 問題点に全力投資ができたことが救いだったらしいです。 本番はせっかくなのでもっと勉強してから挑戦します。 KMCでISUCON勉強会やりましょう。というかKMCONやりましょう。