📂Clojure開発

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

その他

高速化

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)

unchecked-math

inline

コンパイラに対して算術演算の方法を伝えるメタデータ.

メモリ最適化

Profiling Tools

CPU/メモリ使用のプロファイリング.

Topics

HTTP Requestの実行時間

Clojure: Ringの仕様に従ったものならば, response-mapの:request-timeというkeyに実行時間(ms)が格納されている.

🔗References