Clojureをスクリプトとして使う:inlein
inleinはClojureをスクリプトとして使用することを可能にしてくれるツールです。leiningen経由でClojureのコードを実行すると起動が遅いという問題があります。inleineはこの問題を解決(緩和)してくれます。 ちなみに、「スクリプト」と言っているのは「ShellScript」とか「インストール・スクリプト」とか言う時のスクリプトの意味で、ClojureScriptのことではないです。
以降は、inleinのインストールと使い方を簡単に説明し、leiningen
とlein
で速度を簡単に比べます。インストールや使い方については、良ければREADMEの和訳もあわせてご参照ください。
Install
前提
手順
- ここから、リリースされている最新の
inlein
プログラムをDLします。 - DLしたファイルを
$PATH
上に配置。(例えば、~/bin
とか) chmod 755 ~/bin/inlein
のような感じで実行権限を与えればOKです。
より詳しくは、リポジトリのREADMEを参照してください。
How to use
inlein
コマンドは、第一引数で指定されたClojureのスクリプトを実行します。
inlein foo.clj
実際には、スクリプトの実行処理は別のデーモン・プロセスに移譲されます。inlein
を最初に実行するときには自動でデーモンの起動も行われ、2回目以降はデーモンの起動処理はスキップされます。*1
リポジトリを見ると、inleinのエントリポイントとなるclientのJavaコードと、デーモンのClojureコードにディレクトリが分かれていることが分かります。
以下のようにして明示的にデーモンを起動/停止することもできます。
inlein --start-daemon
inlein --shutdown-daemon
inlein --restart-daemon
inlein --ping
: 起動していればPONG
が返ってくるので、デーモンのステータス確認に使う用途だと思われます。
サブコマンド(Taskと呼ぶ)の一覧は引数なしのinlein
かinlein --help
で見れます。
直接inlein
コマンドを叩いてスクリプトを実行することもできますが、実際には、スクリプト先頭にシバン(#!/bin/sh
とかあれ)を書いてスクリプトを直接叩いて実行するほうが想定される使い方だと思います。
以下にinlein用のClojureのスクリプトの例を書きます(READMEのまんまですが)。
$ cat ./primes.clj #!/usr/bin/env inlein ; ↑ シバン行。 ; *nix環境では、これが書いてあってスクリプトに実行権限が与えられていれば、そのファイルを直接実行できる。 ; 依存ライブラリ一覧。クオートしてる以外はleiningenと同様の形式。 '{:dependencies [[org.clojure/clojure "1.8.0"] [com.hypirion/primes "0.2.1"]]} ; ここからが処理内容。nsは無くてもよい。 (require '[com.hypirion.primes :as p]) ; -main関数とかいらなくて、いきなり地の文に処理を書いていける。 (when-not (first *command-line-args*) (println "Usage:" (System/getProperty "$0") "prime-number") ; $0でファイル名が参照できる (System/exit 1)) (-> (first *command-line-args*) (Long/parseLong) (p/get) println)
実際にどれくらい速くなるのか、time
コマンドで簡単に測って比較してみます。環境は MacBookPro の El Captain です。
まずは、inlein
で実行してみます。
$ time ./primes.clj 3 7 ./primes.clj 3 1.18s user 0.19s system 115% cpu 1.187 total $ time ./primes.clj 3 7 ./primes.clj 3 1.18s user 0.17s system 117% cpu 1.155 total $ time ./primes.clj 3 7 ./primes.clj 3 1.19s user 0.18s system 117% cpu 1.175 total
次に、leiningenでプロジェクトを作って実行速度を計測してみます。まず、コードを作ります。
$ lein new inlein-test $ cd ./inlein-test
$ open ./src/inlein_test/core.clj $ cat ./src/inlein_test/core.clj (ns inlein-test.core (:gen-class)) (require '[com.hypirion.primes :as p]) (defn -main [& arg] (when-not (first *command-line-args*) (println "Usage:" (System/getProperty "$0") "prime-number") (System/exit 1)) (-> (first *command-line-args*) (Long/parseLong) (p/get) println))
$ open ./project.clj $ cat ./project.clj (defproject inlein-test "0.1.0-SNAPSHOT" :description "FIXME: write description" :url "http://example.com/FIXME" :license {:name "Eclipse Public License" :url "http://www.eclipse.org/legal/epl-v10.html"} :dependencies [[org.clojure/clojure "1.8.0"] [com.hypirion/primes "0.2.1"]] :main inlein-test.core)
そして、同様に3回計測してみます。
$ time lein run 3 7 lein run 3 2.60s user 0.31s system 111% cpu 2.608 total $ time lein run 3 7 lein run 3 2.62s user 0.31s system 110% cpu 2.638 total $ time lein run 3 7 lein run 3 2.58s user 0.30s system 111% cpu 2.589 total
inleinのほうが倍以上速い、でいいのかな。体感的にあんまり変わらないんだが。。
ちなみに、inleinには直接java
から実行するコマンドを生成する機能があります。inlein --sh-cmd <script-name> <args>...
です。
$ inlein --sh-cmd ./primes.clj 3 java -XX:+TieredCompilation -XX:TieredStopAtLevel=1 -cp /Users/xxxxx/.m2/repository/org/clojure/clojure/1.8.0/clojure-1.8.0.jar:/Users/xxxxx/.m2/repository/com/hypirion/primes/0.2.1/primes-0.2.1.jar '-D$0=./primes.clj' clojure.main ./primes.clj 3
また同様に3回計測してみます。
$ time java -XX:+TieredCompilation -XX:TieredStopAtLevel=1 -cp /Users/xxxxx/.m2/repository/org/clojure/clojure/1.8.0/clojure-1.8.0.jar:/Users/xxxxx/.m2/repository/com/hypirion/primes/0.2.1/primes-0.2.1.jar '-D$0=./primes.clj' clojure.main ./primes.clj 3 7 java -XX:+TieredCompilation -XX:TieredStopAtLevel=1 -cp '-D$0=./primes.clj' 0.90s user 0.13s system 114% cpu 0.900 total $ time java -XX:+TieredCompilation -XX:TieredStopAtLevel=1 -cp /Users/xxxxx/.m2/repository/org/clojure/clojure/1.8.0/clojure-1.8.0.jar:/Users/xxxxx/.m2/repository/com/hypirion/primes/0.2.1/primes-0.2.1.jar '-D$0=./primes.clj' clojure.main ./primes.clj 3 7 java -XX:+TieredCompilation -XX:TieredStopAtLevel=1 -cp '-D$0=./primes.clj' 0.90s user 0.14s system 115% cpu 0.892 total $ time java -XX:+TieredCompilation -XX:TieredStopAtLevel=1 -cp /Users/xxxxx/.m2/repository/org/clojure/clojure/1.8.0/clojure-1.8.0.jar:/Users/xxxxx/.m2/repository/com/hypirion/primes/0.2.1/primes-0.2.1.jar '-D$0=./primes.clj' clojure.main ./primes.clj 3 7 java -XX:+TieredCompilation -XX:TieredStopAtLevel=1 -cp '-D$0=./primes.clj' 0.90s user 0.13s system 115% cpu 0.898 total
生成したjava
コマンドで実行するとさらに速くなりました。inleinが噛まなくなるので当たり前かもしれないですね。
結果をまとめると、
コマンド | 1回目 | 2回目 | 3回目 | 平均 |
---|---|---|---|---|
lein run |
2.60s | 2.62s | 2.58s | 2.60s |
inlein |
1.18s | 1.18s | 1.19s | 1.18s |
java |
0.90s | 0.90s | 0.90s | 0.90s |
という感じなんですが、inleinの良さは速さ以上に、スクリプト的に記述できるところだと思います。具体的には以下の特徴です。
-main
関数とか作らなくて良い。- 依存ライブラリをスクリプト中に含められる。
→ つまり、依存ライブラリがあっても1ファイルで完結させられる。 - 依存ライブラリの記述がleiningenの
project.clj
とほぼ一緒。 - 実行中のスクリプト・ファイル名を、システム・プロパティの
$0
で参照できるようになる。
速度の比較の仕方について、おかしい所があったら教えて頂けると幸いです。
*1:停止していれば起動されます。