日々精進

aikoと旅行とプログラミング

aikoラジオ・TV出演情報まとめ【2017年11月】

11/29に37枚目となるシングル「予告」を発売するaiko

MVも公開になりました.

いろんなFM局で流れてますよねー. (´,,•з•,,`)とぅっとぅるとぅっとぅる🎵🎵🎵という歌い出しが耳から離れません. そんなaikoさんは今週からいろんなラジオ番組に出演するみたいです. 自分でもよくわからなくなってきたのでまとめます. 新規情報が入り次第追記します.

11/15(Wed) FM802 ROCK KIDS 802 -OCHIKEN Goes ON!!–

【放送時間】21:00〜23:48

funky802.com 22時台はまるまるaikoみたい. 楽しみですね.

http://radiko.jp/#!/ts/802/20171115220000

11/16(Thu) FM802 FLiPLiPS

【放送時間】11:00〜15:00 funky802.com aikoの登場は13時台みたいです.

radiko.jp

11/16(Thu) FM OH! Music Bit

【放送時間】16:00〜17:00

www.fmosaka.net

もしかしてパーソナリティは高木雄也さんですかね?

radiko.jp

11/16(Thu) FM OH! OTO-BAKA

【放送時間】18:00〜19:30

www.fmosaka.net

FM OH!の番組にメッセージ送るとaikoのサイン入りグッズがあるみたい.

http://www.fmosaka.net/_ct/17131187www.fmosaka.net

11/17(Fri) FM802 Ciao! MUSICA

【放送時間】12:00-18:00

funky802.com

aikoは14時台の登場です.

radiko.jp

11/22(Wed) あじがとレディオ

【放送時間】24:00配信開始(以後隔週水曜日24時配信予定)

sp.aiko.pcan.us

ついにTeam aikoで会員専用ラジオが! 聴くためには有料会員登録が必須なので、まだの方はお早めに。

11/23(Thu) ニッポン放送 ナインティナイン岡村隆史オールナイトニッポン

【放送時間】25:00〜27:00

aikoが出たのは岡村歌謡祭出演決定の時以来になるのかな?

radiko.jp

11/28(Tue) 日本テレビ系 ベストアーティスト2017

【放送時間】19:00〜22:54

www.ntv.co.jp

去年に引き続き今年も出演決定

11/29(Wed) NHK-FM 赤い公園津野米咲のゆうがたパラダイス

【放送時間】16:40~18:00

www.nhk.or.jp NHK-FMなら、らじる★らじるで聞けそうですね.

まとめ

日付だけ書き出すと,

となります.

余談

新しいアー写, いいと思います.

来月はTOKYO FMのMonthly Artist File -THE VOICE-の担当になりました.

www.tfm.co.jp

定期的なラジオ出演はとってもうれしいですね.

出演ツイートまとめ

ROCK KIDS
FLiPLiPS
Music Bit
OTO-BAKA

www.fmosaka.net

Ciao! MUSICA
FM-OH!での写真

更新履歴

  • 2017/11/17 岡村隆史のANN情報, ツイート等追記
  • 2017/11/22 あじがとレディオ、ゆうがたパラダイス追記

ハンバーグは中身が少し赤い程度が美味しい(ISUCON7予選参加記)

f:id:bath_poo:20171023215708j:plain

ISUCON7の予選に参加してきた.

isucon.net

ISUCONとは

お題となるWebサービスを決められたレギュレーションの中で限界まで高速化を図るチューニングバトル、それがISUCONです。

というコンテスト. レギュレーションの中ならなんでもOKなコンテストである. 今回はそのISUCON7に参加した記録を残しておきたいと思う.

メンバー

チーム名は「中身が少し赤い程度王国(学生)」とした. 言うまでもないと思うが, 「炭焼きレストランさわやか」に行くと聞かれる「中身が少し赤い程度が一番美味しく召し上がれますがいかがですか?」の一文から引用した. メンバーは次の三人.

  • パイセン
    寿司屋では茶碗蒸しを食べる.ローヤルさわやかが好き. げんこつハンバーグは食べたことがない.
  • かみ(このブログを書いている人)
    ISUCON参加言い出しっぺ. aikoとサッカーとげんこつハンバーグが好き.
  • ちゃんきた
    デグーを飼っている. げんこつハンバーグが好き.

ちなみにデグーはこれ. 残念ながら予選にはいなかった. f:id:bath_poo:20171023220410j:plain

開始前

全員車で拾ってもらい, とりあえずお昼ごはんを買って会場に向かう. 会場に向かう途中で開始時間が13時に延期される(台数多いし仕方ない、運営がんばれという気持ちでいっぱいだった)

会場に到着. 今回借りたのはここ→ コワーキングスペース - 福井県産業情報センター
f:id:bath_poo:20171023221055j:plain 会場のセッティングをする. f:id:bath_poo:20171023221237j:plain 無線LAN完備, 壁一面のホワイトボード, スクリーン, プロジェクター, 液晶, AM9:00~PM9:00まで利用可能で無料という学生には優しい会場だった. 来年もここでやりたい. f:id:bath_poo:20171023221256j:plain 開始までご飯を食べたり, d(^_^o)を書いたりして時間を過ごした. このときにもまだセッティングは続いていて, 先輩がひたすらプリンター接続チャレンジをするも失敗していた. 結果あまりなくても困らなかった. その後は始まるまでDiscordのランダムチャンネルをひたすら眺めていたら, お酒やピザをデプロイした人で溢れていた. こっちもピザを頼みたい. ノリが完全にTwitterで面白かった.

やったこと

メンバー3人があまりにも初心者 and 学会や中間発表でなかなか練習の時間が取れないという中だったので, とりあえず今年は雰囲気をつかむ, 来年へつなげるような気持ちで頑張った. 強い気持ちでRuby実装をチューニングすることに決めた. 以下に書いてあることは間違っている可能性も大なので, 優しく見守っていてほしい.

諸々のインストール

  • emacs, vim
  • alp
  • rack-lineprof
  • pt-query-digest
  • netdata
  • htop このあたりを事前に用意していたスクリプトで一括インストール.

githubにえいっ

githubにえいってやりました. (やってもらいました)

静的ファイルをnginxから返す

よくあるやつ. cssやjsなどはnginxで高速に返してしまおうという試み.

location ~ ^/(css|js|favicon.ico) {
  root /home/isucon/isubata/webapp/public;
}

しかしコレが有効になってたりならなかったりしていたような…

nginxのログをLTSVにする.

alpでアクセスログ見るために, nginxのログをltsv形式に変更する.

  log_format ltsv "time:$time_local"
    "\thost:$remote_addr"
    "\tforwardedfor:$http_x_forwarded_for"
    "\treq:$request"
    "\tstatus:$status"
    "\tmethod:$request_method"
    "\turi:$request_uri"
    "\tsize:$body_bytes_sent"
    "\treferer:$http_referer"
    "\tua:$http_user_agent"
    "\treqtime:$request_time"
    "\tcache:$upstream_http_x_cache"
    "\truntime:$upstream_http_x_runtime"
    "\tapptime:$upstream_response_time"
    "\tvhost:$host";

こいつをhttpディレクティブに追加して, serverディレクティブに次の文を追加した.

access_log /var/log/nginx/access.log ltsv;

redis導入

 ログを見てるとやたらと/icons/xxxxxx.pngが遅いことがわかる. なるほど画像ファイルをDBにツッコんでるんだな, コレなんとかしないとダメだなとなり作戦会議.
 何となくredisにツッコんでみたら良いんじゃない?ということになりredisをISUCON本番で触りだす. 何もわからない. 俺達は雰囲気でredisを使っている.
 get '/icons/:file_name'のところで, キャッシュされてたらそれを使うしされてないならdb.xquery()で呼び出してキャッシュするみたいな処理を書く. 正直こんなので動くのか?という不安のほうが大きかった.

redis.hset(file_name, 'img', row['data'])

mysqlの設定

スロークエリを出してクエリを見てみたり, innodb_buffer_pool_size書いてみたりした.

クエリの改善

貼られてないところにインデックスを貼ってもらうなどする. SQL絡みで言えばN+1もまともに予習せずにいったため, 少なくとも自分は全然気づかなかった. 他のメンバーは気づいていたかもしれない.

max_connectionを増やしたかった

増やしたかった. 途中からやけにToo many connections.が頻発してベンチマークが通らなくなったので, とりあえず(ソースをしっかり読まずに)connectionを増やすことを考えた. /etc/my.cnfにconnection数を書いたら良さそうみたいな感じになり書くも, 214から増えなくてしんどかった. 何が悪かったのか今でもよくわかっていない.

反省会

寿司を食べる. 寿司を食べてる最中に結果が出る.

結果

f:id:bath_poo:20171023224337p:plain 最終スコアが33373点. マジか. そんなに行くとは思っていなかった. 全体の121位みたい. 健闘したほうだと思う. 学生だと20番目ぐらいかな? isucon.net

よかったこと

  • 楽しい!
  • 知識を無限に吸収できる気がする(チャット見てるだけでもわからない単語が飛び交っている)
  • 点数上がると楽しい(今回はベンチが全然つまってなかったみたい。快適動作でした。運営がプロ)

反省点

  • 分担壊れる
  • 圧倒的な知識の無さ(Chache-Controlってなんだってレベル)
  • お菓子はもうちょっとしょっぱめも買っとくべき

過去問見た感じruby実装だとunicornだったが, 今回はpumaだった. pumaって何かと若干慌てた. 開始時間が遅れるというトラブルもあったけど, このレベルのコンテストに無料で参加できてしまうのは本当に有り難いし, なによりやっていて本当に楽しかった. 来年はちゃんと準備をして予選に出たい. そして本選へ. 1年精進します. 櫛井さんを始めとする運営に関わった皆さん, 本当に楽しかったです. ありがとうございました.

isucon.net

余談

福井にもさわやかはあります. f:id:bath_poo:20171023225450j:plain

alpコマンドでNginxのログ解析

ISUCONネタです.

環境

Ubuntu 16.04.3 LTS

インストール

$ wget https://github.com/tkuchiki/alp/releases/download/v0.3.1/alp_linux_amd64.zip
$ unzip alp_linux_amd64.zip
$ mv alp /usr/local/bin/
$ alp --help
usage: alp [<flags>]

Access Log Profiler for LTSV (read from file or stdin).

Flags:
      --help                     Show context-sensitive help (also try --help-long and --help-man).
  -c, --config=CONFIG            config file
  -f, --file=FILE                access log file
  -d, --dump=DUMP                dump profile data
  -l, --load=LOAD                load profile data
      --max                      sort by max response time
      --min                      sort by min response time
      --avg                      sort by avg response time
      --sum                      sort by sum response time
      --cnt                      sort by count
      --uri                      sort by uri
      --method                   sort by method
      --max-body                 sort by max body size
      --min-body                 sort by min body size
      --avg-body                 sort by avg body size
      --sum-body                 sort by sum body size
      --p1                       sort by 1 percentail response time
      --p50                      sort by 50 percentail response time
      --p99                      sort by 99 percentail response time
      --stddev                   sort by standard deviation response time
  -r, --reverse                  reverse the result of comparisons
  -q, --query-string             include query string
      --tsv                      tsv format (default: table)
      --apptime-label="apptime"  apptime label
      --reqtime-label="reqtime"  reqtime label
      --status-label="status"    status label
      --size-label="size"        size label
      --method-label="method"    method label
      --uri-label="uri"          uri label
      --time-label="time"        time label
      --limit=5000               set an upper limit of the target uri
      --location=LOCATION        location name
      --includes=PATTERN,...     don't exclude uri matching PATTERN (comma separated)
      --excludes=PATTERN,...     exclude uri matching PATTERN (comma separated)
      --include-statuses=PATTERN,...
                                 don't exclude status code matching PATTERN (comma separated)
      --exclude-statuses=PATTERN,...
                                 exclude uri status code PATTERN (comma separated)
      --noheaders                print no header line at all (only --tsv)
      --aggregates=PATTERN,...   aggregate uri matching PATTERN (comma separated)
      --start-time=TIME          since the start time
      --end-time=TIME            end time earlier
      --start-time-duration=TIME_DURATION
                                 since the start time (now - time.Duration)
      --end-time-duration=TIME_DURATION
                                 end time earlier (now - time.Duration)
      --version                  Show application version.

Nginxの設定

alpはLTSV(Labeled Tab-serapated Values)形式のログファイルを集計するコマンドなので, ログ出力形式をLTSVにする必要がある. /etc/nginx/nginx.confを次のように設定.

user www-data;
worker_processes auto;
pid /run/nginx.pid;

events {
    worker_connections 768;
}

http {

    log_format ltsv "time:$time_local"
                "\thost:$remote_addr"
                "\tforwardedfor:$http_x_forwarded_for"
                "\treq:$request"
                "\tstatus:$status"
                "\tmethod:$request_method"
                "\turi:$request_uri"
                "\tsize:$body_bytes_sent"
                "\treferer:$http_referer"
                "\tua:$http_user_agent"
                "\treqtime:$request_time"
                "\tcache:$upstream_http_x_cache"
                "\truntime:$upstream_http_x_runtime"
                "\tapptime:$upstream_response_time"
                "\tvhost:$host";


    server {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Host $http_host;
    access_log /var/log/nginx/access.log ltsv;

        location / {
            proxy_pass http://127.0.0.1:5000;
        }

        location /stars {
            proxy_pass http://127.0.0.1:5001;
        }
    }
}

そして再起動

systemctl restart nginx.service

何もエラーが出なければ成功している.

ログの見方

-fオプションをつけると, ログファイルを指定して結果を表示することが出来る.

$ alp -f /var/log/nginx/access.log
+-------+-------+-------+-------+-------+-------+-------+-------+--------+-----------+-----------+------------+-----------+--------+---------------------------+
| COUNT |  MIN  |  MAX  |  SUM  |  AVG  |  P1   |  P50  |  P99  | STDDEV | MIN(BODY) | MAX(BODY) | SUM(BODY)  | AVG(BODY) | METHOD |            URI            |
+-------+-------+-------+-------+-------+-------+-------+-------+--------+-----------+-----------+------------+-----------+--------+---------------------------+
|     2 | 0.001 | 0.001 | 0.002 | 0.001 | 0.001 | 0.001 | 0.001 |  0.000 |  1687.000 |  1687.000 |   3374.000 |  1687.000 | GET    | /login                    |
|     1 | 0.001 | 0.001 | 0.001 | 0.001 | 0.001 | 0.001 | 0.001 |  0.000 |  1696.000 |  1696.000 |   1696.000 |  1696.000 | GET    | /register                 |
|    72 | 0.001 | 0.003 | 0.086 | 0.001 | 0.000 | 0.001 | 0.003 |  0.001 |   335.000 |   335.000 |  24120.000 |   335.000 | POST   | /top4aew4fe9yeehu/job/new |
|     2 | 0.087 | 0.099 | 0.186 | 0.093 | 0.087 | 0.087 | 0.087 |  0.006 | 12129.000 | 12143.000 |  24272.000 | 12136.000 | GET    | /keyword/熱塩循環         |
|     5 | 0.640 | 1.020 | 4.252 | 0.850 | 0.640 | 0.804 | 0.948 |  0.130 | 54863.000 | 54878.000 | 219467.000 | 43893.400 | GET    | /                         |
+-------+-------+-------+-------+-------+-------+-------+-------+--------+-----------+-----------+------------+-----------+--------+---------------------------+

各項目の意味は

項目 意味
COUNT アクセス回数
MIN URIへのアクセスにかかる最小アクセス時間
MAX URIへのアクセスにかかる最大アクセス時間
SUM URIへアクセスするのにかかった時間の合計
AVG URIへアクセスするのにかかった時間の平均
P1 1パーセンタイル値
P50 50パーセンタイル値
P99 99パーセンタイル値
STDDEV 応答時間標準偏差
MIN(BODY) そのURIが返した最小のボディサイズ
MAX(BODY) そのURIが返した最大のボディサイズ
SUM(BODY) そのURIが返したボディサイズの合計
AVG(BODY) そのURIが返したボディサイズの平均
METHOD 呼び出したメソッドの種類
URI 計測したURI

間違っていたら指摘してください. パーセンタイル値って初めて知りましたね…

参考リンク

github.com

papix.hatenablog.com

ytsuda.hateblo.jp

ジャニオタじゃない人間が初めてジャニーズのコンサートに行った話【Hey! Say! JUMP I/Oth Anniversary Tour 2017 福井昼公演】

普段aikoのライブにばかり行っている自分だが

muttan1203.hatenablog.com

muttan1203.hatenablog.com

色々あってHey! Say! JUMPのコンサートに行くことになった.

www.j-storm.co.jp

  • もともとジャニーズのライブには行きたかったし, ちょうどいいタイミングでHey! Say! JUMPのライブが有ると聞いてファンクラブに入会しチケットを申し込んだ.
  • 申し込んだ後に知ったのだが10周年コンサートらしく, 競争率がやばいよと脅される.
  • なんなら当落でたのも知らないぐらい意識が低い.
  • 福井公演(8/12)の昼当選. 欲がないため当たった説
  • 連番者電話口で騒ぐ

そんなこんなで初めてジャニーズのライブに行った人間の感想のようなものを書く. にわかもいいところなので, こいつなにも分かっていないなと思いながら優しい気持ちで読んでいただきたい.

8/11 物販のみ参加

  • 当選したのは8/12なので前日は物販にのみ参加.
  • 田んぼの中にサンドームが生えてるの本当に面白い. f:id:bath_poo:20170817145710j:plain
  • この日は陽が強かったので日傘の列がサンドームに向かっていく光景が広がっていた.
    • 巡礼か何か?
  • 会場暑すぎて溶ける
  • 最近みた物販会場がLLRなので, レジの多さにビビる f:id:bath_poo:20170817151513j:plain
  • 「今日は福井県の顔面偏差値上がってるね」という謎の会話をする.
  • 会場に女性ばっかりでアウェー感がすごい.
  • とりあえずTシャツとタオルを買った.
    • Tシャツよりパンフを買ったほうが良かったかもしれない
  • ライブT全然着ている人居なくて戸惑う
  • 買うもの買ったので一旦帰る.
  • 夜はソースかつ丼 f:id:bath_poo:20170817151847j:plain
  • ヨーロッパ軒は美味しい
  • Hey! Say! JUMP LIVE TOUR 2015 JUMPing CARnivalを見て予習する.

8/12 ライブ当日

開演まで

  • 起きてご飯食べて出発
  • 福井駅が今まで見たこと無い感じになっていてビビる
  • 敦賀方面行く電車ってこんなに混むんだ…
  • 鯖江駅におりたつ.
  • タクシー待ち行列が無限に伸びていて, これ歩いたほうが早いのでは?と一人一人に説いてあげたかった.
  • だんだんテンションがおかしくなりお腹が痛くなる.
  • いっその事帰るかという気持ちになる.
  • Hey! Say! JUMPは本当にこの世に存在するのかという議論をする.
  • ライブTシャツ着てる族が瀕死
  • 会場と写真を撮るなどして過ごす.
  • 顔が書いてあるうちわと会場を一緒に写している人がたくさん居たのは何だったんだろう.
  • ベクトルが書いてあるうちわを持っている人が多い(ファンサくれということらしい)
    • ここ来るまでファンサってなんだよというレベルだった
  • 入場が始まる. 今回入場時に席が分かるのでメチャクチャ緊張する.
  • 席を見た.
  • アリーナ ブロックB2 10列 近すぎる. メッチャいい席じゃねーか.(星あたり) f:id:bath_poo:20170817153143p:plain
  • セットすごい凝ってるんだななどと思う
  • 開演まで「ヤバい」「近い」「どうしよう」をひたすら繰り返す

開演

時系列順ではない.

  • 映像かっこいいですね.
  • でもまだ存在するかわからないですねみたいな感じになる. ほんとに出て来るのこの人たち.
  • ペンライトがきれいなのはよくあるけど, あんだけうちわがあると壮観だった.
  • Ultra Music Power… 知念くんが風をきるやつから始まる. コレは流石に知っている.
  • マジで会場がキャーキャー言っていて, コレがジャニーズかという気持ちになった.
  • 全面ディスプレイにおもっきり歌詞が出ているの面白い. Mステ見てる感じだった.
  • 正直あんまり覚えてないところが多いのだが, 山田涼介が引くぐらい顔整っていた.
  • 自分の席からだと山田と中島の2名がよく見えた.
  • 伊野尾が遠い(つらい)
  • ウィークエンダー聞けたのでハッピー
  • 途中高木さんが口パクになっていた.(OVERのとき?)
  • ロック・リー中島はよく似合っていた.
    • あの会場でロック・リー知らないという人どのくらいいたんだろう.
  • 伊野尾の謎の上履きへのこだわり(BESTコーナー)
  • 母親に魂を売った有岡(BESTコーナー)
  • 山田涼介が全裸で振り付けした曲I/O
  • Vampireへの壮大なフリ
  • 中島裕翔はドラムができて, 岡本圭人はギターを弾けるらしい. 完璧じゃねーか.
  • この人たちは仲が良さそうで見ていてほっこりした.
  • トークで福井がDisられる. もう慣れているのでなんとも思わなかった.
  • ロッコで2/3ぐらいは来たと思う. 薮さんのファンになった.
  • 八乙女, 中島はファンサマシーン.
  • 知念人気がすごい.
  • Come On A 知念’s House
  • Hey! Say! JUMPと同じ時代に生まれてきた僕らはツイてるって感じだった.
  • 有岡くんの前髪消滅した.
  • 挨拶9人やるのか…
  • 締めはやぶひか.
  • 充実の2.5hだった.
  • でもな途中のソースカツ丼の画像が福井のじゃないのはツメが甘いですよ.
  • ヒゲダンスしてっていううちわあったけど何だったんだ.

終演後

  • 相方が薮グッズを追加購入していた.
  • 自分も買おうか迷ったけどやめました.
  • 居酒屋で打ち上げ f:id:bath_poo:20170817163515j:plain f:id:bath_poo:20170817163520j:plain f:id:bath_poo:20170817163524j:plain
  • 日本酒と魚がうまい.
  • らじらーを聞く.
  • Hey! Say! JUMP LIVE TOUR 2014 smartを見る.
  • 3日間で3ライブ分見たのか…

感想

想像よりも楽しいライブ

 上にも書いたとおり, 今までジャニーズのライブに行ったことがない. 正直なところちょっと不安を抱えてライブ会場に向かっていた. ライブが始まってみるとそんな心配は吹っ飛び, JUMPワールドに引き込まれた. 顔が良いのは勿論のこと, 舞台がメチャクチャ凝っていてコレがジャニーズなのかと感じた. そして何より踊りだ. 9人が連動して魅せる踊りは, 最高に決まっていて格好良かった. 楽曲も何かしらで聴いたことある曲が多かったし, いいなと思う曲もいくつかあった. 何がいいたいかというと大変満足しているということだ. 隣にいた人がメチャクチャ楽しそうにしていて, こっちまで幸せな気持ちになった.

推しの出現

Hey! Say! JUMPをよく知らない頃, なんとなく伊野尾がいいなとか想っていた. 可愛いからね. 会場に行ってみて良いなと感じたのは, 薮, 八乙女, 有岡だろうか.

  • 薮くん
    スタイルがいい. 笑ったときの顔が良かった.
  • 八乙女くん
    ロッコ乗ってファンサしているときの表情がよかった.
  • 有岡くん
    歌ってる時の有岡くんいいじゃないの.

みんな素晴らしかったけどね.

9人が立つステージ

9人もステージにいるとどこを見てていいやら… 散らばると色んな所をキョロキョロしていて不審者だったと思う. それに他のファンのかたはうちわやペンライト, 双眼鏡も装備していて忙しいなと思った. 固定のステージ, バックステージだけではなくセンターステージもある. またトロッコがあるので, 席がある程度後ろや2階であっても近くで見れるのはジャニーズライブのいいところだと感じた. aikoだとなかなかそうもいかなかったりするからね.

総括

 次チケット取れるかわからないけれどもぜひ行きたいと思えるライブだった. 一番最初のライブがHey! Say! JUMPで, しかも記念コンサート, 席もいいという初参戦にしては文句のつけようがない環境であった.
 aikoのときと違って, 歌だけではなくて踊りもあるし人数も多くて舞台上が非常に賑やかだった. これからも1番はaikoだと思うけど, ジャニーズのコンサートってのは楽しいということが分かっただけでも良かった. この先の公演もHey! Say! JUMPが何事もなく走りきれることを願っています.
 そしてなにより, このコンサートに行くきっかけを作ってくれた友人に感謝したいとおもう. ありがとう.

ISUCONの過去問に挑戦するための環境構築をした

いい感じにスピードアップするコンテストの過去問をやりたい. isucon.net 過去問のセットアップで少々躓いたのでメモ.

環境

Vagrantをインストールする

Vagrantを使用するのでインストール.

www.vagrantup.com

これのインストールについては省略.

Vagrantファイルを用意する

matsuuさんが公開されているVagrantファイルを得ます.

github.com

今回はisucon6-qualifierをセットアップします.

$ mkdir ISUCON
$ cd ISUCON
$ git clone https://github.com/matsuu/vagrant-isucon.git
$ cd vagrant_isucon/isucon6-qualifier

Vagrantファイルのあるところまで移動し, 起動

$ vagrant up

しばらくまって, プロビジョニングが終わったら, ベンチマーク(bench)とアプリケーション側(image)にログイン.

$ vagrant ssh bench
$ vagrant ssh image

参考実装の切り替え

初期ではPerlでの実装になっているため, Rubyへの実装へ変えようという話に. ISUCON6の予選マニュアルをみてたところ

github.com

なるほど, systemdで管理されてると言うのが分かる. よし, systemctlで変更しようと思いきや

$ systemctl stop isuda.perl.service

isuconというユーザーのパスワードが分からず断念.
あまりにもわからなかったので, matsuuさんに尋ねる. isuconユーザーでなくubuntuユーザー(vagrant sshしたときのユーザー)でsudo systemctl ... すればいけるんじゃない?とのことなのでやってみる.

$ whoami
ubuntu
$ sudo systemctl stop isuda.perl.service

いけた. isuconユーザーにデフォルトパスワードが設定されていなかったのが原因っぽい. DMで突然聞いたのに大変丁寧に対応してくださった. その後isuconユーザにパスワードを付加した.

$ sudo passwd isucon

こんな感じでなんとかできた.

アプリケーションの画面

f:id:bath_poo:20170804182330p:plain トップページ思いなあなどと思う. アドレスはifconfigして適当に見た.

ベンチマークの実行

$ sudo -i -u isucon
$ cd isucon6q
$ ./isucon6q-bench -target http://<アドレス>
2017/08/04 17:04:55 start pre-checking
2017/08/04 17:05:04 pre-check finished and start main benchmarking
2017/08/04 17:05:55 benchmarking finished
{"pass":true,"score":0,"success":941,"fail":20,"messages":["リクエストがタイムアウトしました (POST /keyword)","リクエストがタイムアウトしました (POST /login)","リクエストがタイムアウトしました (POST /stars)"]}

Rubyでの実行結果です. scoreは0. Perlだと初期実装だけでも点がでた.
丁寧に対応してくださったmatsuu氏に感謝します.

iTunesの再生回数をAppleScript + Ruby + gnuplotで表示してみる

AppleScriptRubyを使って, iTunesの再生回数を取得しグラフ化してみた.
 やろうと思ったきっかけは, 自分がaikoの曲をどれだけ聞いているか知りたかったから. よって, このあと出て来る例はすべてaikoを検索している.

developer.apple.com

AppleScriptで曲と再生回数を取得

AppleScriptなにもわからない. 雰囲気でAppleScriptを書いている.

tell application "iTunes"
        # 変数はsetで宣言
    set lst to {}
    
        # repeat with 変数 in オブジェクト(foreach)
    repeat with t in (file tracks whose artist contains "aiko")
        copy ((name of t as string) & " " & played count of t as string) to the end of lst
    end repeat
    
    return lst
end tell

分からなさすぎて検索しまくった. これを実行すると, 以下のような出力を得られる.

f:id:bath_poo:20170703140028p:plain

それっぽい結果が出ている. このスクリプトを次に作るrbファイルと同じ階層においておく.

Rubyでデータを加工

AppleScript辛い…しか言えない体になっていたので, ここでRubyにバトンタッチ.

コマンドを実行

とりあえず先ほどのAppleScriptを実行しないと話にならないので, ``を使って実行し結果を得る.

# scriptの実行
elements = `osascript itunes.scpt`.chomp.split(",").map{ |item| item.strip }
曲名ごとにカウントする

 aikoはアルバムを14枚(ベストアルバム込み), シングルも36枚ある. もちろん曲の被りがあるので, それは同じものとしてカウントしたい.
 そこでHashを使ってカウントする.

# 曲名ごとにカウント
hash = Hash.new(0)
elements.each do |elem|
  next if (elem.index("instrumental"))
  len = elem.rindex(" ")
  name = elem[0..len]
  time = elem[len..elem.length]
  hash[name] += time.to_i;
end

今回instrumentalはカウントしないことにしたので, 以下のように弾く処理を書いている.

next if (elem.index("instrumental"))

 また, “Do you think about me?"や"be master of life"のように空白が1つ以上出現する文字列もあるため, rindexで一番最後の空白を基準に曲名と回数を分割することにした.

len = elem.rindex(" ")
name = elem[0..len]
time = elem[len..elem.length]

そして, その名前をkeyとして回数を加算する.

hash[name] += time.to_i;
再生回数でソートする

 先程カウントしたハッシュをソートする必要がある. HashのSortは[key, value] を要素とする配列の配列に変換して,それをソー トした配列を返す。

# 再生数でソート
songs = hash.sort do |a, b|
  b[1] <=> a[1]
end

gnuplotで描画

 画面に文字列として出力するだけではわかりにくいので, gnuplotを使って表示することにした. ここではgnuplotのインストールについては省略する.

ラベルと回数を取得

ラベルと回数をそれぞれ別の配列として持たないといけないので, それ用の配列を作る.

# Gnuplot用にラベルと回数を別々に配列へ格納
labels = []
times = []
songs.each_with_index do |item, idx|
  labels << "\"#{item[0]}\""
  times << item[1]
  break if idx > 50
end

あまりに多いと格納できないので, 上位50曲程度で打ち切っている.

描画する

以下のgemを使ってRubyから描画する.

gnuplot | RubyGems.org | your community gem host

# 描画
Gnuplot.open do |gp|
  Gnuplot::Plot.new(gp) do |plot|
    plot.terminal "aqua font 'ヒラギノ角ゴ Pro W3,10'"
    plot.title  "視聴回数"
    plot.yrange "[0:300]"
    plot.ylabel "再生回数"
    plot.bmargin "8"
    plot.style "fill solid border -1"
    plot.xtics "rotate by -90"
    plot.unset "key"

    plot.data << Gnuplot::DataSet.new([labels, times]) do |ds|
      ds.using = "2:xtic(1)"
      ds.with = 'boxes lc rgb "orange"'
      ds.title = "再生回数"
    end
  end
end

 本当は, set terminal png enhanced font ‘GothicBBB-Medium-EUC-H, 10'のような感じにしたかったのだけどできなかったので, terminalに出してスクリーンショットに妥協した.
 最終的なソースコード以下のようになる.

require "gnuplot"

# scriptの実行
elements = `osascript itunes.scpt`.chomp.split(",").map{ |item| item.strip }

# 曲名ごとにカウント
hash = Hash.new(0)
elements.each do |elem|
  next if (elem.index("instrumental"))
  len = elem.rindex(" ")
  name = elem[0..len]
  time = elem[len..elem.length]
  hash[name] += time.to_i;
end

# 再生数でソート
songs = hash.sort do |a, b|
  b[1] <=> a[1]
end

# Gnuplot用にラベルと回数を別々に配列へ格納
labels = []
times = []
songs.each_with_index do |item, idx|
  labels << "\"#{item[0]}\""
  times << item[1]
  break if idx > 50
end

# 描画
Gnuplot.open do |gp|
  Gnuplot::Plot.new(gp) do |plot|
    plot.terminal "aqua font 'ヒラギノ角ゴ Pro W3,10'"
    plot.title  "視聴回数"
    plot.yrange "[0:300]"
    plot.ylabel "再生回数"
    plot.bmargin "8"
    plot.style "fill solid border -1"
    plot.xtics "rotate by -90"
    plot.unset "key"

    plot.data << Gnuplot::DataSet.new([labels, times]) do |ds|
      ds.using = "2:xtic(1)"
      ds.with = 'boxes lc rgb "orange"'
      ds.title = "再生回数"
    end
  end
end

ここまで書いたらGemfileを書く.

$ bundle init

bundle initを実行してGemfileを作る. Gemfileは

# frozen_string_literal: true
source "https://rubygems.org"

gem 'gnuplot'

とする. ここまできたらinstallをする.

$ bundle install --path vendor/bundle

これでインストールされるので, いよいよ実行.

$ bundle exec ruby ***.rb

実行結果(曲単位)

実行結果は次のようになった.

f:id:bath_poo:20170703143619p:plain

ランキングを見てみると, 上位5曲は以下のようになっていた.

  1. 恋のスーパーボール
  2. シアワセ
  3. 夢見る隙間
  4. もっと
  5. 花火

 意外とbe master of lifeとか聞いていないんだなという気持ちになっている. 1位が恋のスーパーボールなのは, いっとき狂ったように聞いていた時があったので適当. 夢見る隙間が3位にいるのは意外すぎる. ちなみに6位は何時何分なので, 発売時期を考慮するとMay Dreamは割りと聞いているということがわかった.
 これだけでは面白くないので, 先ほどのスクリプトを改造してアルバムごとの回数を見てみる.

アルバムごとに集計

AppleScriptを以下のように変更し(name→album)

tell application "iTunes"
    set lst to {}
    
    repeat with t in (file tracks whose artist contains "aiko")
        copy ((album of t as string) & " " & played count of t as string) to the end of lst
    end repeat
    
    return lst
end tell

集計部分のソースコード(Ruby)を多少変更した.

require "gnuplot"

# scriptの実行
elements = `osascript itunes.scpt`.chomp.split(",").map{ |item| item.strip }

# 曲名ごとにカウント
hash = Hash.new(0)
albums = ["小さな丸い好日", "桜の木の下", "夏服", "秋 そばにいるよ", "暁のラブレター", "夢の中のまっすぐな道", "彼女", "秘密", "BABY", "まとめ I", "まとめ II", "時のシルエット", "泡のような愛だった", "May Dream"]
elements.each do |elem|
  next if (elem.index("instrumental"))
  len = elem.rindex(" ")
  name = elem[0..len-1]
  time = elem[len+1..elem.length] 
  if albums.include?(name) then # アルバムであるかどうか
    hash[name] += time.to_i;
  end
end

# 再生数でソート
songs = hash.sort do |a, b|
  b[1] <=> a[1]
end

# グラフの上限を設定
# 1400なら2000にするなど.
tmp = songs[0][1].to_s
max_val = tmp[0].to_i + 1;
max_val *= (10 ** (tmp.size - 1))

# Gnuplot用にラベルと回数を別々に配列へ格納
labels = []
times = []
songs.each_with_index do |item, idx|
  labels << "\"#{item[0]}\""
  times << item[1]
  break if idx > 50
end

# 描画
Gnuplot.open do |gp|
  Gnuplot::Plot.new(gp) do |plot|
    plot.terminal "aqua font 'ヒラギノ角ゴ Pro W3,10'"
    plot.title  "視聴回数"
    plot.yrange "[0:#{max_val}]"
    plot.ylabel "再生回数"
    plot.bmargin "8"
    plot.style "fill solid border -1"
    plot.xtics "rotate by -90"
    plot.unset "key"

    plot.data << Gnuplot::DataSet.new([labels, times]) do |ds|
      ds.using = "2:xtic(1)"
      ds.with = 'boxes lc rgb "orange"'
      ds.title = "再生回数"
    end
  end
end

まとめⅠとⅡが微妙に違って集計されていなかった. つらいね.

実行結果(アルバム単位)

f:id:bath_poo:20170703151533p:plain  まとめ2枚と時のシルエット, May Dreamがずば抜けている. 確かによく聞いてるし, なによりまとめから入った人間なのでそれはそう.
 暁のラブレター全然聞いていないことに気づく. iTunesのカウントは, おそらく最後まで聞いた曲を1回とカウントしてるので, 終わったと思って飛ばしてしまうと1回とカウントされてないのではないだろうか. 彼の落書きとか結構聞いてると思うのだが…  先日書いたブログでも, 割と力を入れて書いたところが上位に来ているのでそれだけ聞いていたんだと実感した. グラフィカルに見られるのはいいことだ.

muttan1203.hatenablog.com

 あまりにも汎用性の低いコードすぎるので, albumでまとめるのか全部含めるのかなどなど色々更新していきたい.
 あとaikoはいいぞ.

OpenCVでボロノイ分割とドロネー分割

OpenCVを使ってボロノイ分割とドロネー分割をした.

実行環境

特徴点の抽出

画像から特徴点を取り出すのに, Shi-Tomashiのコーナー検出法を利用する.

void goodFeaturesToTrack(InputArray image,       // 入力画像  
             OutputArray corners,      // 検出されたコーナー  
             int maxCorners,         // コーナーの最大数  
             double qualityLevel,      // クオリティレベル(0.0~1.0)
             double minDistance,       // コーナー間の最小ユーグリット距離
             InputArray mask=noArray(),
             int blockSize=3,   
             bool useHarrisDetector=false,   
             double k=0.04 )

具体的には

// Shi-Tomasi Corner Detector
int maxCorners = 0;
vector<Point2f> corners;
goodFeaturesToTrack(img, corners, maxCorners, 0.01, 50);

のようにして行う.

Subdiv2Dの初期化

分割を行うために必要なSubdiv2Dを初期化する.

Mat out = imread(filename); // 出力用
Size size = out.size();
Rect rect(0, 0, size.width, size.height);
Subdiv2D subdiv(rect);

ボロノイ図を書く

ボロノイ分割, ドロネー分割を行う前に, 先程取得した点をSubdiv2Dに追加する.

subdiv.insert(corners);

ボロノイ図を書くには, getVoronoiFacetListを使う.

void cv::Subdiv2D::getVoronoiFacetList(const std::vector< int > &idx,
                                       std::vector< std::vector< Point2f > > &facetList,
                                       std::vector< Point2f > &facetCenters 
                                      ) 

このメソッドを呼び出すと, facetListにベクトルの集合が格納される. 今回はdraw_voronoiという関数を定義した.

void draw_voronoi(Mat &img, Subdiv2D &subdiv){
    vector<vector<Point2f> > facetList;
    vector<Point2f> facetCenters;
    subdiv.getVoronoiFacetList(vector<int>(), facetList, facetCenters);

    for(auto &trig : facetList){
        vector<Point2f>::iterator it = trig.begin();
        Point2f p1 = *it;
        it++;
        while(it != trig.end()){
            Point2f p2 = *it;
            line(img, p1, p2, Scalar(255, 255, 255));
            p1 = p2;
            it++;
        }
    }
}

もうちょっときれいにかけないものか.

ドロネー図を書く

方法としては2つ存在していて,

  1. getEdgeListメソッドですべての辺を取得する方法
  2. getTriangleListメソッドで三角形を取得する方法

が存在する. 今回はgetEdgeListを使う.

void cv::Subdiv2D::getEdgeList(std::vector< Vec4f > &edgeList)const

引数として与えたedgeListに辺が格納される.(変数名のとおりだが)今回は, draw_delaunayという関数を定義した.

void draw_delaunay(Mat &img, Subdiv2D &subdiv){
    vector<Vec4f> edges;
    subdiv.getEdgeList(edges);

    for(auto &edge:edges){
        line(img, Point2f(edge[0], edge[1]), Point2f(edge[2], edge[3]), Scalar(0, 255, 0));
    }
}

ソースコード

以下に今回使用したソースコードを示す.

#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>

using namespace std;
using namespace cv;

void draw_point(Mat& img, Point2d p, Scalar color){
    circle(img, p, 5, color, -1);
}

void draw_delaunay(Mat &img, Subdiv2D &subdiv){
    vector<Vec4f> edges;
    subdiv.getEdgeList(edges);

    for(auto &edge:edges){
        line(img, Point2f(edge[0], edge[1]), Point2f(edge[2], edge[3]), Scalar(0, 255, 0));
    }
}

void draw_voronoi(Mat &img, Subdiv2D &subdiv){
    vector<vector<Point2f> > facetList;
    vector<Point2f> facetCenters;
    subdiv.getVoronoiFacetList(vector<int>(), facetList, facetCenters);

    for(auto &trig : facetList){
        vector<Point2f>::iterator it = trig.begin();
        Point2f p1 = *it;
        it++;
        while(it != trig.end()){
            Point2f p2 = *it;
            line(img, p1, p2, Scalar(255, 255, 255));
            p1 = p2;
            it++;
        }
    }
}

int main() {
    string filename = "ファイルのパス";

    cout << CV_VERSION << endl;
    // 対象画像をグレイスケールで読み込み
    Mat img = imread(filename, IMREAD_GRAYSCALE);


    // Shi-Tomasi Corner Detector
    int maxCorners = 0;
    vector<Point2f> corners;
    goodFeaturesToTrack(img, corners, maxCorners, 0.01, 50);

    // 出力用ファイルを作成
    Mat out = imread(filename);
    for(auto &point : corners){
        draw_point(out, point, Scalar(0,0,255));
    }

    // Delaunay分割のためのSubdiv2Dを初期化
    Size size = out.size();
    Rect rect(0, 0, size.width, size.height);
    Subdiv2D subdiv(rect);

    // 点を追加する
    subdiv.insert(corners);

    draw_voronoi(out, subdiv);
    draw_delaunay(out, subdiv);

    namedWindow("Delaunay");
    imshow("Delaunay", out);

    waitKey();

    return 0;
}

サンプル画像lena.pngにかけてみる.

参考サイト

www.learnopencv.com

schima.hatenablog.com

OpenCV: cv::Subdiv2D Class Reference

Shi-Tomasiのコーナー検出とGood Features to Track(追跡に向いた特徴) — OpenCV-Python Tutorials 1 documentation