カジュアルにClojureのファイルを実行する
Leiningenは便利だけども、プロジェクトを作らないと実行できないのがたまに面倒くさい。 そこで、ここを参考に実行用のファイルを作ってみた。
その1
Leiningenはclojure本体を~/.m2/repository/org/clojure/clojure/1.6.0/clojure-1.6.0.jar
に持ってくるので、、
こんな感じのファイルを~/bin/clj
とでも名付けて(実行可能にして)置いた。
#!/usr/bin/env sh exec java -cp ~/.m2/repository/org/clojure/clojure/1.6.0/clojure-1.6.0.jar clojure.main $1
こんなノリで実行できる。
clj hoge.clj
その2
ちょいと調べてみたら、lein-execなるLeiningenのプラグインが見つかった。
~/.lein/profiles.clj
のプラグインベクタに[lein-exec "0.3.4"]
を追加してあげる(バージョンは変わりうる)。
これで実行できる。
その1のやつと違って~/.m
以下のモジュールにパスが通っている感ある。
lein exec hoge.clj
欠点として、起動が少し遅いのと、コマンド入力時にファイル名が補完できない点がある。
起動が遅いのは諦めるとして、ファイル名補完は上の戦法をパクることで解決した。
~/bin/clj
を次のように書き換えただけ。
#!/usr/bin/env sh exec lein exec $1
ひとまずこれで良さそうな気がする。 実際にこれを使うかは全く予想がつかない(たぶん使わない気がするw)。
git diff --statの横幅
git diff --stat
の出力をパイプにぶち込むときの横幅の指定方法をすぐ忘れるからメモっておく。
デフォルトでは80文字っぽい。
git help diff
で確認。
--stat[=<width>[,<name-width>[,<count>]]]
こんな感じでdevelopとの比較を何らかのコマンドに渡す。
git diff --stat=128 develop | hoge
VimでClojureを良い感じに使うためのアレ
が、変化が速く?日本語で見つかる情報が若干古く、Vimでの環境構築に苦労したのでメモ。 もちろん自分用のメモなのでMac向けな内容。 ここの内容を自分が実際にやった内容を思い出しながら書いた。
到達目標
- Leiningenでプロジェクト作ったり何やら
- VimClojureからNailgunに繋いでVim内でREPLとか特定の行を評価したり ←これがメイン
Leiningen
Homebrewで入る。簡単。
brew install leiningen
# プロジェクトを作る lein new hoge # 依存関係を解決済みなREPLを起動する lein repl # あとはへるぷ嫁 lein help
続いて、Leiningenのプラグインを入れる。
~/.lein/profiles.clj
を編集し、lein-tarsier
を読み込むようにしておく。
{:user {:plugins [[lein-tarsier "0.10.0"]]}}
lein deps
して依存関係を解決したらlein vimclojure
サブコマンドが使えるようになる。
これでLeiningen側の準備はOK!
Nailgun
Nailgunは、JVMを立ち上げっぱなしにしておくことでJVMを起動する時間を短縮してプログラムの実行時間を短くしてくれる君…という認識で合っているのかな。 怪しいけど、ひとまず「起動を速くしてくれる」機能が間違いなく備わっているはず。
Homebrewにパッケージがあるけど、これを使ってはならない。 VimClojureから接続するときに、なんか上手く繋がらない。 これでかなり時間を取られてしまった感あるのでご注意を。
VimClojureのリポジトリをダウンロードし、解凍しmake
する。
出力されたng
が実行可能であるか確認し、パスが通っている場所に放り込む。
(参考にしていたドキュメントでは作者氏のサイトへの直リンクが貼ってあったけど、それを転載するのはアレなのでリポジトリのリンクを貼ってある)
cd kotarak-vimclojure-22bbb523b401/client make mv ng ~/bin/
これでNailgunの準備はOK!
VimClojure
NeoBundleでVimClojure
を入れる。
NeoBundle 'VimClojure'
こんな感じで設定する。
let vimclojure#WantNailgun = 1 " Nailgunを要求する let vimclojure#NailgunClient = "ng" " Nailgunのクライアントコマンド let vimclojure#HighlightBuiltins = 1 " ビルトイン系をハイライト let vimclojure#ParenRainbow = 1 " カッコの色を階層に応じてカラフルにする
これでVimClojureの準備はOK!
いざ、試す
プロジェクトを作る。
lein new hoge
cd hoge
lein vimclojure
コマンドを実行して放置する。
ちなみに、下記の表示が出て止まる。
$ lein vimclojure
Starting VimClojure server on localhost, port 2113
Happy hacking!
この状態で、別のウィンドウにてVimを開く。 エラーが表示されなければ、Nailgunへ接続できているはず。
src/hoge/core.clj
を次のように変更し(末尾にfoo
を使うコードを追加しただけ)、おもむろに\ef
と入力する!
(\
は<Leader>
のこと)
(ns hoge.core) (defn foo "I don't do a whole lot." [x] (println x "Hello, World!")) (foo "hoge")
するとVimのウィンドウが2つにわかれ、上部にはこのような表示が出る。
これはsrc/hoge/core.clj
を評価した結果だ!
\ef
は、REPLでファイル全体を評価した結果を表示してくれる。
; Use \p to close this buffer! nil #'hoge.core/foo hoge Hello, World! nil
(foo "hoge")
の行にカーソルがある状態で\et
とすると、その行のみを評価することができる。
; Use \p to close this buffer! hoge Hello, World! nil
\sR
で、現在開いているファイルのネームスペースにてREPLがVim上で起動する!
普通のREPLと同じように評価したい式を入力してエンターで評価してくれる。
Clojure hoge.core=> (foo "hoge") hoge Hello, World! nil hoge.core=>
ちなみに、\sr
ではuser
ネームスペースにてREPLを起動する。
Clojure user=> (use 'hoge.core) nil user=> (foo "hoge") hoge Hello, World! nil user=>
この辺が使えればだいぶ捗るようになるはず。 デフォルトのマッピングが自分には合わないので、押しやすそうなマッピングに変更する(そのうち...)。
カーソルがある部分のドキュメントを検索したり、ソースを表示したり検索したり、他にもいろいろできる。
あとは:h vimclojure.txt
を見て頑張るぞい!
Perlでベンチマーク
Perl Advent Calendar 2014の枠が空いていたので、ただのメモですが10日目の記事として晒すことに。
今さらかなり基本的なことだけど、Perlでのベンチマークの実行方法を調べて、適当にいろいろ試してみたメモ。
Benchmarkモジュール
基本的にはperldocを見れば良い。 ゆとりなのでPerldoc.jpにて。
いろいろ関数があるけども、よく使いそうな雰囲気なのは次の2つな気がした。
timethis
: 特定のコードの実行速度を測るcmpthese
: 複数のコードの実行速度を測りつつ比較する
timethis
DateTime
は遅いって言われているけど、実際にどれくらい遅いのか試しに測ってみる。
現在の時間を取得して1日足すという処理を例に。
timethis
の1つ目の引数は、2つ目の引数の処理を実行する回数を示している。
第2引数には、CodeRefかevalしたい文字列を指定する。
正の数を指定した場合は「実行回数」を、負の数を指定した場合は「最低実行時間」を示している。
0
はデフォルト値で、-3
を指定したときと同じ挙動を示す。
use Benchmark; use DateTimeX::Factory; my $dt = DateTimeX::Factory->new(time_zone => 'Asia/Tokyo'); timethis 0, sub { $dt->now->add(days => 1); };
実際に実行してみた結果が次の出力。 CPU時間3秒くらい使って9331回実行して、毎秒約3千回の速度で実行できたらしい。
timethis for 3: 3 wallclock secs ( 3.13 usr + 0.00 sys = 3.13 CPU) @ 2981.15/s (n=9331)
cmpthese
これだけだと、この処理にかかる時間がわかるだけで比較ができないし、実行環境によって速度も変わってくる。
ので、cmpthese
を使って比較を行ってみる。
cmpthese
では、1つ目の引数はtimethis
と同じで、2つ目の引数にはHashRefにて実行したいコードと、そのコードの名前を渡してあげる。
use Benchmark qw/ cmpthese /; use DateTimeX::Factory; use Time::Moment; my $dt = DateTimeX::Factory->new(time_zone => 'Asia/Tokyo'); cmpthese 0, { 'DateTime' => sub { $dt->now->add(days => 1) }, 'Time::Moment' => sub { Time::Moment->now->plus_days(1) }, };
実際に実行してみた結果が次の出力。 もはや、DateTimeが遅いとかじゃなくてTime::Moment速すぎワロタ状態。
Rate
の項目が実行速度を示していて、それより右側は他との比較を示している。
この例だと、Time::Moment
と名付けた処理は、DateTime
と名付けた処理よりも42690%速い、すなわち428倍の実行速度があることがわかる。
逆に、DateTime
はTime::Moment
よりも100%遅い(差が大きすぎて丸められてしまった!)、ことがわかる。
ちなみに、時間系モジュールの速度比較はここで詳しく行っている(Time::Moment
まじ速い)。
Rate DateTime Time::Moment DateTime 3034/s -- -100% Time::Moment 1298354/s 42690% --
適当にいろいろ測ってみる
ちらっとどこかで見かけた関数呼び出しのオーバーヘッドを測ってみる
このコードでお試し。
sub one { Time::Moment->now->plus_days(1) } sub two { one() } sub three { two() } cmpthese 0, { one => \&one, two => \&two, three => \&three, };
one
に元の処理を記述し、two
, three
では無駄に関数呼び出しを挟んでいる。
上記のTime::Moment
の処理を実行した場合、元の処理がかなり速いため、関数呼び出しのオーバーヘッドが如実に表れている。
自分の環境だと140000[1/sec] = 7[μsec]くらいが関数呼び出しにかかる時間らしい。
Rate three two one three 1047791/s -- -12% -22% two 1191842/s 14% -- -11% one 1338232/s 28% 12% --
次に、上記のDateTime
の処理で置き換えた版を実行してみる。
my $dt = DateTimeX::Factory->new(time_zone => 'Asia/Tokyo'); sub one { $dt->now->add(days => 1) } sub two { one() } sub three { two() } cmpthese 0, { one => \&one, two => \&two, three => \&three, };
当たり前だが、今度はどれも誤差レベルのしか生まれなかった。 関数の中身自体が遅く、呼び出し回数が少ない場合には、関数呼び出しのオーバーヘッドはさほど問題では無くなる。
Rate one three two one 2753/s -- -2% -3% three 2810/s 2% -- -1% two 2825/s 3% 1% --
シュワルツ変換によるソート速度向上を測定してみる
配列の各要素のsha512の16進数表現を文字列としてソートする場合を作って試した。 (そんな場合が実際にあるかはわからないが、単に重そうな処理をかませたかっただけ)
それぞれのラベルでは次の処理を行う
normal
: 愚直にsort
ブロック内でsha512_hex
を実行するschwartz
: シュワルツ変換で頑張る
use Benchmark qw/ cmpthese /; use Digest::SHA qw/ sha512_hex /; my @array = (1..100); cmpthese 0, { normal => sub { my @sorted = sort {sha512_hex($a) cmp sha512_hex($b)} @array; }, schwartz => sub { my @sorted = map {$_->[1]} sort {$a->[0] cmp $b->[0]} map {[sha512_hex($_), $_]} @array; }, };
この場合、シュワルツ変換を使った方が約7倍速くなった。
Rate normal schwartz normal 305/s -- -86% schwartz 2186/s 617% --
ソート対象の配列の要素数を100個から10個に減らした場合でも、まだまだシュワルツ変換を使った方が3倍速く、有効そう。
Rate normal schwartz normal 7590/s -- -67% schwartz 23057/s 204% --
こんな感じで
実行速度が実際にどれくらいなのか、AとBの実装ではどれくらい速度差があるのか、、、などを知りたいときは、ちゃんとベンチマークを実行し、数字ベースで比較できると良いな、という感想。