在ARef上调整奇怪的计算时间

时间:2014-04-12 14:46:00

标签: clojure

我有一个函数foo:

(defn foo [a b] (* a b)) ; <- 2 ms per 10K
(foo 3 8) ; <- 24

我希望能够传递代理,原子或引用。

(defn bar [a b] (* @a @b)) ; <- 3.5 ms per 10K

但那么混合文字和代理呢?

(defn aref? [x] (isa? (class x) clojure.lang.ARef))
(defn foo [a b] (let [a (if (aref? a) @a a)
                      b (if (aref? b) @b b)]
                  (* a b))
(def x (ref 3))
(def y (ref 8))
(foo 3 8); <- 120 ms per 10K
(foo 3 y); <- 71 ms per 10K
(foo x 8); <- 73 ms per 10K
(foo x y); <- 6 ms per 10K

但那些是真正时髦的运行时间。我尝试更改foo中分支的顺序,这显然与它无关。为什么我的新foo评估文字的时间要比ARefs长20倍?

1 个答案:

答案 0 :(得分:3)

实际上,ez121sl提到性能差异来自isa?的使用。查看其源代码,您会发现它使用内置的global-hierarchy(如果没有提供)并递归检查bases类的child(基类)是否存在是定义的parent类。

使用instance?(使用封面后面的Java instanceof?)会产生更明智的时间。

(defn aref? [x]
  (instance? clojure.lang.ARef x))

(defn foo [a b] (let [a (if (aref? a) @a a)
                      b (if (aref? b) @b b)]
                  (* a b)))
(def a (ref 3))
(def b (ref 8))
(time (dotimes [i 10000] (foo a b))) ; ~ 9ms
(time (dotimes [i 10000] (foo a 8))) ; ~ 2.7ms
(time (dotimes [i 10000] (foo 3 b))) ; ~ 2.7ms
(time (dotimes [i 10000] (foo 3 8))) ; ~ 1.7ms