$PATHを改行しつつ表示する

普通にこれで良いと思った。

$ echo $PATH | sed -e 's/:/\n/g'

が、ダメ。 :nに置換されてしまう。 ダブルクオートじゃないからダメなのか...?

$ echo $PATH | sed -e "s/:/\n/g"

が、ダメ。 きーたのコメント欄に答えがあった。

BSD系のsedでは、\nで改行文字に置換してくれないようだ。 結局きーたのコメントのオススメ通り、ぱーるにて。

$ echo $PATH | perl -pe 's/:/\n/g'

カジュアルに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)。

VimでClojureを良い感じに使うためのアレ

Clojure良いよ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倍の実行速度があることがわかる。 逆に、DateTimeTime::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の実装ではどれくらい速度差があるのか、、、などを知りたいときは、ちゃんとベンチマークを実行し、数字ベースで比較できると良いな、という感想。