plenv が良い感じ
perlbrew 使ってたんだけど、tokuhirom がつくってる plenv にスイッチした。
perlbrew よりシンプルでなにやってるかわかるのがなにより。 というか perlbrew がよく分からなすぎるという話もある。
plenv だとさらに .perl-version
というファイルに perl のバージョンをいれておくと、そのディレクトリ以下では自動的にそのバージョンの perl が使われるという機能があって、これがだいぶはかどる。
perlbrew で入れた各種 perl は -g
つけわすれて SEGV したときこまってて別途 --as=5.14.2-debug
みたいのをつくってたけど、 その経験を生かして今度は最初から
$ plenv install 5.14.2 -D=DEBUGGING=-g
な感じで全部入れるようにした。ちなみにこの install オプションはまだドキュメントになってないのでまだ変わるかもしれないから、この記述は参考にしないようおねがいいたす。
perlbrew と比べるといったんラッパーをかます分 bootstrap が遅いというのがあって、 flymake とかだと結構気になるけど、 perl のバージョン検出の仕組みはシンプルだから elisp でかき直すのもすぐできそう。
Emacsの設定を見直し、 el-get の distributed セットアップに統一
いままで秘伝のタレのような設定を使い続けていたのだが、どうもしばらく使っているとEmacsがCPUを非常に食うようになってくる。 flymakeの設定かなー?とかほかにもいろいろ怪しげなところを削ってみたりしたがわからず、だましだまし使っていた。
そういう理由もあってEmacs設定をリセットしたいと思っていたので、Emacs実践入門や Advent Calendar を読んでいたら el-get というパッケージマネージャがなかなかよさそうだと思った。
これ、存在自体はまえから知っていたけど秘伝のタレベースな自分は使わないだろうなーとスルーしていた物だが、 ドキュメント(info)を読んでみると思った以上によさげだと言うことが分かった。 Mac 使ってる人にわかりやすくいうなら Emacs 用の homebrew という感じ。
なかでも 6.3 Distributed Setup
で紹介されてる方法を使うと、他の環境に設定を持って行ったときも初回起動時にパッケージをよしなにインストールしてくれて自動的に同じ環境にしてくれる。これはなかなか便利。
というわけで作り直した設定がこちら、まだ perl と clmemo 周りの最小限の設定しかないけれども。
~/.emacs.d/
以下に置かなくても動くようにつくってあるので、
$ git clone git://github.com/typester/emacs-config.git
# no-window の場合
$ emacs -q -l emacs-config/init.el
# Cocoa の場合
$ /Applications/Emacs.app/Contents/MacOS/Emacs -q -l emacs-config/init.el
とするともりもりパッケージのセットアップが始まって(時間はかかるけど)僕と同じ設定で Emacs を試せる状態になるはず。
Emacs24 前提と、 el-get のために git コマンドが exec-path
に必要。
このように非常にカジュアルに人の設定を試せるのはかなり革命的。 (もちろん複数の環境で設定を同期するのにも革命的)
なのでみんながこれ前提でセットアップしてくれたら最高だなーと思いますのでこれを読んでる Emacs ユーザーの方々におかれましては el-get ベースな設定を一考していただければ幸いでございます。
MacでのBluetoothホストコントロールデバイスの優先順位の変更
毎回ググるのでメモ。
OSX は USBのドングルとかBluetoothホストコントローラとかをつけたときの挙動を、ファームウェアの NVRAM ってところに bluetoothHostControllerSwitchBehavior
というキーで値を設定してあげることによって以下の3種類のうちから決めることができる。
never
: 外付けのホストコントローラにスイッチせず、つねにビルドインコントローラを使うalways
: ビルドインコントローラからdetachして外付けのホストコントローラにスイッチするdefault
: 外付けホストコントローラがApple以外のときはビルドインからdetachして外付けデバイスを使う
デフォルトはもちろん default
。
で、 BT4LE に対応したドングルを持っていれば、BT4積んでない昔の MacBook なんかでも CoreBluetooth つかったり開発したりできる。
さらに、あえてここで never
を設定して外付けのホストコントローラを使わない設定にすると、 余っている外付けホストコントローラを iOS Simulator から利用することができる。
この設定を変えるには nvram
ってコマンドを使う:
$ sudo nvram bluetoothHostControllerSwitchBehavior="never"
とかでOK。
詳しくは
Technical Note TN2295: Testing Core Bluetooth Applications in the iOS Simulator
参照。
Any::Moose とか幻想やったんや
Any::Moose
は Moose
と Mouse
のラッパーで、バックエンドがどちらになるかは use Any::Moose
した時点で Moose
or Mouse
がロードされているかによって変わる。
どこかで use Moose
していたら use Any::Moose
は use Moose
としてうごくし、 そうじゃない場合は use Mouse
としてうごく。
これはなんのためにあるかというと、MouseとMooseの両方のモジュールを混ぜて使うと壊れるからなんだけど、 そもそも混ぜて使って問題になるのは Mouse のクラスを Moose で extends したりとか、そういう Moose 周りの機能を相互に使おうとした場合だけであって、混ぜて使っていてもそれぞれのクラスを普通に使い合う分にはなんの問題もない。
また、昔はruntimeの速度にそこまで性能差はなかったということもある。(make_immutable
してたら Moose もそこまで遅くない)
しかし現在では gfx マジックによって Mouse は激しく高速化されており、runtime での速度差もかなりある。 したがって、Moose的な意味で相互利用する場合でなければ、Any::Moose
が勝手に Moose になるとうれしくない場合がほとんどだ。
さらに、MooseとMouseの非互換によって、バックエンドがMouseのときはうまく動いていたコードが、 どこからMooseモジュールを使用したことによってバックエンドがMooseに切り替わった瞬間に動かなくなるという事例もある。
結論として Any::Moose
つかわないで Mouse
を直で使う、で誰も困らないなーとおもってるのでそうしていこうと考えております。
もし混ぜることがあったとしても Moose からつかうときは non Moose なクラスとして扱うようにすれば別に問題ないでしょう。(僕はそもそもMoose使わないけどw)
Redis布教活動報告 ISUCON 編
最近 Test::RedisServer
とかもろもろつくっててばれてるかもしれませんが、だいぶ Redis 期にありまして、最近の趣味は?っていう問いにはだいたいRedisのソースを読むことですってなくらいなのですが、 memcached とかシンプルな KVS と比べるとだいぶ機能が豊富なので使い方を迷ったりとかそういう事例もあり、周りにもう少し使える人を増やさなければ僕の書いたコードが属人化しててつらい感じになるなーっていうわけで、 布教活動をおこなっておりまして、その一環として ISUCON2 に参加してきましたのでその報告です。
前回の優勝チームに混ぜてもらった感じでだいぶついてる感じもしますが、見事連覇を果たせ、懇親会でも redis redis と連呼してきたのでだいぶ興味持った方も多いのではないでしょうか!
そんな布教活動視点から ISUCON を振り返ってみたいと思います。
Redis がつかえるかどうか
開始直後はチーム間でのコンフリクトをさけるために git push
で rev サーバーに反映ってのだけは設定しましたが、そのあとはすぐボトルネックそうなところで Redis に置き換えられる物はないか探していました。
すぐに購入トランザクションが見つかります。Redis には Set 型という集合を表す型があり、それを使うと集合のなかから排他的に一つランダムでとりだして消すというのをとても効率的に実装することができます。 ISUCON ではこのランダム条件が(チェッカーがバグでスルーしていたみたいですけど)なかなかきびしかったところだと思いますが、そこもするっと解決です。 なお、順番に購入という条件であれば双方向リストの List 型を使うのが正解でしょう。こちらも先頭or末尾からの取得&削除を排他的に行うことができます。
ついでに購入履歴も Redis にもたせておけばそこから、サイドバー(recent_sold
)とCSV吐き出しもどちらも行えそうだなと思いました。
ここまででとりあえずこの購入トランザクションだけでも Redis にうつしたら DB ロックする必要がないのでだいぶ良さそうだなと思い、redisブランチやりますとチームに宣言します。
書き換えの流れ
そしてもりもり書き換えました。
POST /admin
でMySQLにデータを流し込んだあとに stock のデータをすべて redis にコピーする。(stock:{variation_id}
というキーのSet)POST /buy
で対象の Set から一つ取り出し、order_history
という List にアーティスト名などの付属情報込みな辞書情報をPush。- サイドバーは
order_history
から最新10件を取得するように変更 - CSV書き出しは
order_history
をまるっと書き出す感じ - その他もろもろテンプレに渡してるデータをよしなに変更
といった感じですが、アプリのテストコードがないのでだいぶ変更が手探りな感じでつらかったです。とくにテンプレ周り。なおしてベンチ止まって、ベンチのエラー見て修正という…。 そんなつらいかんじや実際の作業内容は redis ブランチのコミットログを参照いただくと分かるかなと思います。
他になんかあるか?
コード見てるだけだとわからなかったので fujiwara さんにボトルネック探してもらいつつ、自分はとくにやることもないので非同期の実装を模索しておりました。 この時点ではforkモデルの限界なんじゃなかろうかと思っていて非同期にしてベンチ取ってみるか、みたいな感じでしたが、Perlで非同期にすると書き直しになるし、nodeの実装いじっててもうまく動かなくてすぐにあきらめてしまったんですが、それは fujiwara さんがテンプレのレンダリングボトルネックを見つけてくれて、そちらを直したら今までのアプリでも十分スループット(reverse proxy <-> app 間)が出ており、非同期全然意味ありませんでした。
まぁそうだよね、IOウェイトが問題になるような場合以外は非同期意味ないなーとそのとき思った次第です。
テンプレキャッシュ
組長が見つけてくれたチケットの座席表のレンダリングのボトルネック。これも Redis でキャッシュするようにしました。 Redis は単純なKVSとしてみても十分高速なのでこういう用途にも使えます。
データ永続化
これは本家ドキュメントよくまとまっております。
スナップショット(dump)と追記ログ(appendlog - MySQLのbinlog的な)の二種類の永続化方法があり、dumpはバックアップ用、復元をappendlogから、ってのがオススメな方法。
appendlog は基本的にはプロトコルログなんですが、大きくなるとバックグラウンドで最適化されて小さくなります。デフォルトでは fsync が1秒ごとに走るので最悪そこまでのデータは保全される。 書き込み毎に sync って言うのも設定でできますがパフォーマンス的につらいことになるでしょう。
これがISUCONのレギュレーションを満たしているかは定かではありませんがとりあえず fujiwara 組では1秒fsyncなappendlogでデータ永続化をしました。
また、メモリいっぱい使い切っているような大きなデータセットの場合に appendlog するは不安感ありますが、自分もまだ試したことがないので、近いうちに試したいと思っております。
まとめ
あんまりおすすめしまくっていると勘違いしちゃう人もいるかもしれないのでいちおう補足しておきますが、単純に MySQL を Redis に置き換えたら速くなる、っていうことはないです。 MySQLがいいところ、Redisがいいところ、(もちろんその他のツールも)どちらもあるので、 Pros and Cons やトレードオフを考えた上で選択するのが大事です。
僕自身も Redis つかっているといっても運用上のノウハウなどまだまだうちも足りていないのでそこはこれから精進していきたいと思います。
しかし、ISUCON楽しいですね。 #isucon のログによると
11:47 < typester> すでにだいぶたのしい
といっており、ここからずーーーっと終了までたのしかったですね。
あと珈琲ありがとうございました!
番外: Xslate の速さを実感
家帰って他の言語の実装をうごかしてみると、明らかに座席表のレンダリングが遅いんです。ブラウザで見てボトルネックって分かるレベル。 でも、Perl の実装をブラウザで見た人はあのページの遅さはテンプレートエンジンじゃなくてブラウザがレンダリングするのに時間かかってる、くらいにしか感じない気がする。 今回の罠は Xslate の速さと言っても良いのではないでしょうか。
これは gfx++ といわざるを得ませんね!
コマンドラインでネットワーク設定のネットワーク環境のところを変える
ネットワーク周りの設定は networksetup
コマンドでいろいろできるみたいで、 man をみるとどうも環境設定でできることはほとんどここからもできる模様。
んで、ネットワーク環境というのは
-listlocations
List all network locations.
-getcurrentlocation
Display the name of the current set.
-createlocation location [populate]
Create a set with the user-defined-name name and optionally populate it with the default services.
-deletelocation location
Delete the set.
-switchtolocation location
Make the specified set the current set.
この辺が該当するようだ、切り替えるだけなら
$ networksetup -switchtolocation Hoge
で OK。
バッテリーのこり60秒で通知の話
僕のトークでなんか気になった人がいるとのことなので僕の使っているスクリプトを置いておきますね。
#!/usr/bin/env perl
use strict;
use warnings;
use utf8;
use Cocoa::BatteryInfo;
use Cocoa::EventLoop;
use Cocoa::Growl ':all';
growl_register(
app => 'Battery Notifier',
notifications => ['NotifyLastOneMinite', 'NotifyTimeRemaining'],
);
Cocoa::BatteryInfo::time_remaining_handler {
my $sec = Cocoa::BatteryInfo->time_remaining_estimate;
return unless $sec =~ /\d/;
if ($sec <= 60) {
growl_notify(
name => 'NotifyLastOneMinite',
title => 'バッテリー切れまで',
description => sprintf '残り %d 秒', $sec,
);
}
else {
my $time;
if ($sec >= 60*60) {
$time = sprintf '%d 時間 %d 分 %d 秒',
int($sec / (60*60)), int(($sec % (60*60)) / 60), $sec % 60;
}
elsif ($sec > 60) {
$time = sprintf '%d 分 %d 秒',
int($sec / 60), $sec % 60;
}
else {
$time = sprintf '%d 秒', $sec;
}
growl_notify(
name => 'NotifyTimeRemaining',
title => 'バッテリー切れまで',
description => sprintf '残り %s', $time,
);
}
};
Cocoa::EventLoop->run;
これを LaunchAgent でログイン時に自動で立ち上がるようにしております。
で、これをうごかしているとバッテリー残り時間が変化したタイミングで Growl 通知が来るのですが、 普通は NotifyTimeRemaining
という通知が来ます。もしそのときに残りが60秒いないだったらその代わりに NotifyLastOneMinite
という通知が来ます。
毎回通知されるのはうざいので僕は NotifyTimeRemaining
というほうは Growl の設定でオフにしています。
で、だいたい使っている感覚としては60秒でまず通知が来て、 その後もういちど、残り0秒という通知がきて、その直後にハイバネートする、というような感じのようです。
YAPC::Asia 2012
今回は前夜祭でも話すことになって、始めて前夜祭からの参加になったけど、なかなかおもしろかった。
トークは二つってのはかなり久しぶりだけど、どちらも20分だし、YAPC向けになにか特別ネタを用意したというわけではなく、普段やってることや考えていることを話しただけでだいぶがんばってない感じのトークです
UV - libuv binding for Perl
Perlハッカーは息をするようにCPANモジュールを書く
というようなトークたちです。
今回は基本的にホールで電源ある席を陣取り、コード書きつつトークを見る、というスタイルで参加。
Padre の話は聞けなかったんだけど、Adam Kennedy さんはなんというか堂々としていて自信に満ちていてかっこよかった。あう言う大人になりたい。 Cocoa ネイティブ対応はなんか気が向いたら見てみようかなーって言う感じ。
Ingy はなんかすごいおじいちゃんっぽくなってておもしろかった。
NYTProf の話は Phase0 のスピリチュアルなところが結構響いた。これは見せたい人が一杯いるのでスライドを待つ。 実際の最適化の話はおおむね賛成できるかなーというところ。pidのオプションとかしらなかったなー。
mizzy さんはほんとにいいひとだな! って思いました。
あと、antipop さんはきもいすごい
Cocoa::BatteryInfo has been released!
本日二回ほどバッテリー切れで黒い画面をみて、「システムのバッテリー残量警告でるのはやすぎなんだよなーもっとぎりぎりの残量通知が欲しいよなー」、とか思いつつバッテリー周りのAPIドキュメントを眺めていたら気がついたらCPANモジュールができていた…。
Cocoa::BatteryInfo - Getting battery informations on your Mac - metacpan.org
単純なバッテリー情報の取得の他にも、 Cocoa::EventLoop
との合わせ技で OS からのバッテリー関連通知を受け取るという機能もあるので残量監視スクリプトなど作るのに最適です。
というとネタモジュールな感じだけど、ノートブックのインターナルバッテリー以外にも OS X Server に接続している UPS の情報などもとれたりしますのでわりとそっち系では実用モジュールなのではないかと思われる。
JSON::Types ってのを書いた
JSON-Types-0.01 - variable type utility for JSON encoding - metacpan.org
Perl から JSON 吐くときに、ここは絶対数値で(文字列で)だしたい、みたいなときに
use JSON;
print encode_json({
number => $num + 0,
string => $str . '',
bool => $bool ? \1 : \0,
});
みたいにすることがあるかもしれませんが、これってやっぱりハックなので可読性悪いしわかりにくいってことで、
use JSON;
use JSON::Types;
print encode_json({
number => number $num,
string => string $str,
bool => bool $bool,
});
みたいな感じで、まぁ内部的にやってることは同じなのですが可読性を上げていこう!というような趣旨のモジュールです。
追伸: Acme::Hidek のリリースで hidek さんの誕生日を知りました。おめでとうございます!