repl-serverを立ち上げてclojureの起動時間を短縮
はじめに
clojure,scala,jrubyとかjvmの上に乗った言語は起動がとても遅い。*1
そんなわけで、これをどうにかしたいと思っていたのでした。
emacsclientのようにserverを立ち上げてそれにアクセスするという形にすれば起動は一回で済みそうです。
まだまだ改善の余地がたくさんあるけれど。途中経過。
todo
how to use
clojure nrepl.clj 12345 #server gosh nrepl-client.scm -e "(+ 10 20 30)" #client ### # time clojure -e "(+ 10 20 40)" # 1.06s user 0.05s system 117% cpu 0.946 total # time gosh nrepl-client.scm -e "(+ 10 20 40)" # 0.04s user 0.01s system 115% cpu 0.043 total #
一応、20倍くらいはやくなっている。
server
(import '(java.net ServerSocket Socket SocketException) '(java.io InputStreamReader PrintWriter) '(clojure.lang LineNumberingPushbackReader)) (defn nrepl [s] (let [sin (-> (. s getInputStream) (InputStreamReader.) (LineNumberingPushbackReader.)) sout (-> (. s getOutputStream) (PrintWriter. true))] (binding [*in* sin, *out* sout, *err* sout] (clojure.main/repl :print (comp (fn [_] (prn 'end-of-output)) prn) ;end-of-output is terminator :prompt (fn []))))) (defn- on-thread [fun] (doto (Thread. fun) (.start))) (defn start-server [port] (printf "start with port=%d\n" port) (let [ss (ServerSocket. port 1)] (on-thread #(try (while true (nrepl (.accept ss))) (catch SocketException e))))) (defn- main [args] (if-let [[port & rest] args] (start-server (Integer/parseInt port)) (println "<program> <port-number>"))) (main *command-line-args*)
client
'end-of-outputまで読み込むという仕様は綺麗じゃないかも?
#!/usr/bin/env gosh (use gauche.parseopt) (use gauche.net) (define (send data port) (let1 s (make-client-socket 'inet "localhost" port) (call-with-client-socket s (lambda (in out) (write data out) (newline out) (flush out) (let loop () (let1 line (read in) (cond [(eq? 'end-of-output line) (flush)] (else (write line) (newline) (loop))))))))) (define (main args) (let-args (cdr args) [(port "p|port=i" 12345) (expr "-e|exp=s")] (let1 data (if expr (read-from-string expr) (read)) (send data port))) 0)