我有一个用Lisp(SBCL)实现的版本,在0.001秒内运行12个样本。然而,这个版本(在clojure中)需要超过1.1秒。 我应该怎么做才能使这个代码像原始的Lisp版本一样快速运行?
为了确保,我的数字不包括启动repl和其他人的时间。并且是sbcl和clojure的时间函数。(是的,我的笔记本电脑是基于原子的老式)
这个应用程序将用于repl,而不是在单个应用程序中编译,因此在基准测试之前运行一千次似乎没有意义。
哦,fbars是这样的:[[10.0 10.5 9.8 10.1] [10.1 10.8 10.1 10.7] ...],这是 股票的开盘价高低收盘价。
(defn- build-new-smpl [fmx fmn h l c o]
(let [fmax (max fmx h)
fmin (min fmn l)
fc (/ (+ c fmax fmin) 3.0)
fcd (Math/round (* (- fc o) 1000))
frd (Math/round (* (- (* 2.0 c) fmax fmin) 1000))]
(if (and (> fcd 0) (> frd 0))
[1 fmax fmin]
(if (and (< fcd 0) (< frd 0))
[-1 fmax fmin]
[0 fmax fmin]))))
(defn binary-smpls-using [fbars]
(let [fopen (first (first fbars))]
(loop [fbars fbars, smpls [], fmax fopen, fmin fopen]
(if (> (count fbars) 0)
(let [bar (first fbars)
[_ h l c _] bar
[nsmpl fmx fmn] (build-new-smpl fmax fmin h l c fopen)]
(recur (rest fbars) (conj smpls nsmpl) fmx fmn))
smpls))))
=============================================== =
谢谢。我设法将1000次迭代的差异设为0.5秒(SBCL为1.3秒,Clojure为1.8秒)。主要因素是我应该创建fbars不是懒惰但是作为具体(?)向量或数组,这解决了我的问题。
答案 0 :(得分:6)
您需要使用适当的基准测试库;标准的Clojure解决方案是Hugo Duncan的Criterium。
原因是JVM上的代码开始以解释模式运行,然后最终由JIT编译器编译;它是JIT编译之后的稳态行为,您希望在分析阶段进行基准测试而不是行为。然而,这是非常棘手的,因为JIT编译器可以在无法操作的地方优化无操作,因此您需要确保您的代码导致无法优化的副作用,但是您仍然需要运行它在循环中获得有意义的结果等 - 快速和肮脏的解决方案只是不削减它。 (请参阅Criterium的自述文件中链接的Elliptic Group, Inc. Java benchmarking article,以便对所涉及的问题进行扩展讨论。)
循环在长度为1000的向量中列出的两个样本,在我的机器上的Criterium基准测试中得到~327μs的时间:
(require '[criterium.core :as c])
(def v (vec (take 1000 (cycle [[10.0 10.5 9.8 10.1] [10.1 10.8 10.1 10.7]]))))
(c/bench (binary-smpls-using v))
WARNING: Final GC required 4.480116525558204 % of runtime
Evaluation count : 184320 in 60 samples of 3072 calls.
Execution time mean : 327.171892 µs
Execution time std-deviation : 3.129050 µs
Execution time lower quantile : 322.731261 µs ( 2.5%)
Execution time upper quantile : 333.117724 µs (97.5%)
Overhead used : 1.900032 ns
Found 1 outliers in 60 samples (1.6667 %)
low-severe 1 (1.6667 %)
Variance from outliers : 1.6389 % Variance is slightly inflated by outliers
一个非常好的基准实际上会涉及一个有趣的数据集(所有不同的样本,最好来自现实世界)。
答案 1 :(得分:3)
当我使用1000个样本运行时,我会在46毫秒内得到答案,不过这里有一些关于更快地制作clojure代码的常见技巧:
启用反射警告:
(设置!警告反射为真)
添加类型提示,直到反射警告消失,这不是问题
使它成为一个惰性序列,因此您不必在ram中构建大量序列,这会导致大量的GC开销。 (在某些情况下,这会增加开销,但我认为这是一个不错的主意)
在这种情况下,看看incanter是否可以为你完成这项工作(虽然这可能是作弊)
创建结果的时间,不包括repl打印它所花费的时间。