Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/clojure/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Clojure 如果协议函数返回的常量不是';t缓存_Clojure_Benchmarking - Fatal编程技术网

Clojure 如果协议函数返回的常量不是';t缓存

Clojure 如果协议函数返回的常量不是';t缓存,clojure,benchmarking,Clojure,Benchmarking,我正在写一封信。它的一部分是定义如何使用系统的协议。协议的一部分是返回系统运行所需组件的函数。它可以提炼为以下内容: (defprotocol System (required-components [sys] "Returns a map of keys and initial values of components that the System requires to operate.") 因为这个函数返回的值实际上是一个常量,所以我认为缓存它可能是一个好主意,因为它可能需

我正在写一封信。它的一部分是定义如何使用
系统的协议。协议的一部分是返回系统运行所需组件的函数。它可以提炼为以下内容:

(defprotocol System
  (required-components [sys]
    "Returns a map of keys and initial values of components that the System requires to operate.")
因为这个函数返回的值实际上是一个常量,所以我认为缓存它可能是一个好主意,因为它可能需要每秒+60次。为了判断这是否有区别,我编写了以下测试:

(defrecord Input-System1 []
  System
  (required-components [sys] {:position [0 0] :angle 0}))

(def req-comps
  {:position [0 0] :angle 0})

(defrecord Input-System2 []
  System
  (required-components [sys] req-comps))
然后在REPL中,我运行了以下时间测试:

(let [sys (->Input-System1)]
  (time-multiple 1000000000
    (required-components sys)))

(let [sys (->Input-System2)]
  (time-multiple 1000000000
    (required-components sys)))
(下面是时间倍数的代码)

奇怪的是,
Input-System1
始终比
Input-System2
快:2789.973066ms,而上次运行时为3800.345803ms

理论上,版本1不断地重新创建组件映射,而版本2只引用预定义的值,这让我觉得很奇怪

我尝试通过删除协议来重新创建:

(defn test-fn1 []
  req-comps)

(defn test-fn2 []
  {:position [0 0] :angle 0})

(time-multiple 1000000000
  (test-fn1))

(time-multiple 1000000000
  (test-fn2))
但这一次,结果几乎相同:3789.478675ms与3767.577814ms

这让我相信它与协议有关,但我不知道是什么。这是怎么回事?我知道,考虑到测试的数量,1000ms是相当微不足道的,所以我不打算在这里进行微优化。我只是好奇

(defmacro time-pure
  "Evaluates expr and returns the time it took.
  Modified the native time macro to return the time taken."
  [expr]
  `(let [start# (current-nano-timestamp)
         ret# ~expr]
     (/ (double (- (current-nano-timestamp) start#)) 1000000.0)))


(defmacro time-multiple
  "Times the expression multiple times, returning the total time taken, in ms"
  [times expr]
  `(time-pure
     (dotimes [n# ~times]
      ~expr)))

在任何一种情况下,您的映射都是一个常量,在类加载期间创建(它是静态已知的,因此在方法调用期间不会创建新对象)。另一方面,缓存的情况会花费您额外的间接性—访问变量

证明:

(def req-comps
   {:position [0 0] :angle 0})

(defn asystem-1 []
   {:position [0 0] :angle 0})

(defn asystem-2 []
   req-comps)
(不管我们是否处理协议,函数的编译都是一样的,这样在编译后的代码中更容易找到它们。)

请参阅-它只返回预先计算的常量

public final class core$asystem_2 extends AFunction {
    public static final Var const__0 = (Var)RT.var("so.core", "req-comps");

    public core$asystem_2() {
    }

    public static Object invokeStatic() {
        return const__0.getRawRoot();
    }

    public Object invoke() {
        return invokeStatic();
    }
}

额外调用
getRawRoot()

谢谢。为什么第二个测试与第一个测试不一致?如果是这样的话,我希望他们会同意。因为结果非常接近(var获取非常便宜),所以时间的波动可能会更大。如果您有疑问,我将向您展示编译后的代码。我认为您是对的,但我仍然很好奇为什么在第一次测试中差异会如此显著。我多次运行这两个测试,结果总是一样的(给或一百毫秒)。JIT,无论在操作系统中发生什么,几乎都是按照星星排列的方式。10-20%的差异并不奇怪。
public final class core$asystem_2 extends AFunction {
    public static final Var const__0 = (Var)RT.var("so.core", "req-comps");

    public core$asystem_2() {
    }

    public static Object invokeStatic() {
        return const__0.getRawRoot();
    }

    public Object invoke() {
        return invokeStatic();
    }
}