Clojureの性能最適化や高速化についてのトピックまとめ.
Benchmarking Tools
clojure.core/time
組込み関数. 実行時間を出してくれる.
https://clojuredocs.org/clojure.core/time
🔧hugoduncan/criterium
よくみかけるのでおそらくデファクトスタンダード.
quick-bench 関数で手早く簡易レポートをみることができる.
(require '[criterium.core :as criterium])
(criterium/quick-bench (reduce + (map #(/ % 100.0) (range 100))))
Evaluation count : 135024 in 6 samples of 22504 calls.
Execution time mean : 5.511216 µs
Execution time std-deviation : 1.115539 µs
Execution time lower quantile : 4.468957 µs ( 2.5%)
Execution time upper quantile : 7.265588 µs (97.5%)
Overhead used : 9.320178 ns
その他
- https://github.com/ptaoussanis/tufte
- 最近開発が盛ん.
高速化
main.coreでAOT(gen-class)
Clojureは動的コンパイルのため, いわゆるJIT方式でJavaコンパイルをするが, AOT compilation をするには gen-class をつかう.
Clojure - Ahead-of-time Compilation and Class Generation
これをnamespaceの宣言でつかうことによってJavaのクラスを事前に生成するようになる. Java Classを生成することのメリットはいろいろあるようだが, たとえば xxxx.coreというプロジェクトのメインnamespaceに定義された -main関数をclojure cliから実行する場合, gen-classを宣言しておくことによってプログラム実行の起動が速くなる.
型ヒント(Type Hinting)
Clojureは動的型付け言語だが動的型付け言語のJavaの上に構築されているため, 暗黙的な型はリフレクションによって型推論されている.
そのため型hintを設定することで型推論の効率化をすることができ, コンパイルや実行速度の最適化をすることができるかもしれない.
Clojureでは, ^ という記号を Record/Type/Protocol/Java Classに設定して 変数を宣言することで型ヒントを実現する.
ref: [Clojure] 型を指定すると速い | Basic Werk
Clojureは型ヒントを使ってコンパイラを助けることで, パフォーマンスの重要なコード領域に対してリフレクションを避けることができる.
ClojureはローカルコンテキストにおけるJavaの基本型を使った高速な計算や算術演算をサポートする. すべてのJava基本型(int, float, long, double, boolean, char, short, byte)がサポートされている。
プリミティブヒント(primitive hiting)
プリミティブヒントは boxing を避けるためのもの.
(defn ->spread ^double [^double ask ^double bid]
(- ask bid))
無検査演算
Clojureは無検査演算が使える. unchecked-add. 型ヒントでも十分高速化できるが, unchecked-addをつかうともう少しだけ算術演算は高速化できる.
unchecked-math
namespaceで無検査例外を有効化する.
(set! *unchecked-math* true)
inline
コンパイラに対して算術演算の方法を伝えるメタデータ.
メモリ最適化
- Running Clojure programs on constrained memory - /dev/solita
- 少ないメモリでどう動かすか.
Profiling Tools
CPU/メモリ使用のプロファイリング.
Topics
HTTP Requestの実行時間
Clojure: Ringの仕様に従ったものならば, response-mapの:request-timeというkeyに実行時間(ms)が格納されている.