当前位置 : 主页 > 网络安全 > 测试自动化 >

clojure – 以“let”形式定义函数的性能问题

来源:互联网 收集:自由互联 发布时间:2021-06-22
在let表单中定义匿名函数并重复调用外部函数是否有任何性能损失?像这样: (defn bar [x y] (let [foo (fn [x] (* x x))] (+ (foo x) (foo y)))) 与将foo定义为单独的函数相比,如下所示: (defn foo [x] (
在let表单中定义匿名函数并重复调用外部函数是否有任何性能损失?像这样:

(defn bar [x y]
  (let [foo (fn [x] (* x x))]
    (+ (foo x) (foo y))))

与将foo定义为单独的函数相比,如下所示:

(defn foo [x] (* x x))
(defn bar [x y] (+ (foo x) (foo y)))

我理解foo的词法范围在这两种情况下是不同的,我只关心foo函数是否会在多次调用bar时反复定义.

我猜答案是否定的,即没有惩罚,但是clojure是怎么做到的?

谢谢!

让本地方法:

foo只编译一次(顶层表单时).这个编译的结果是一个实现clojure.lang.IFn接口的类;实际的body存在于该类的invoke(Object)方法中.在运行时,每次控制到达引入foo本地的bar中的点时,都会分配该类的新实例;对foo的两次调用使用该类的实例.

这是在REPL中证明“单个编译”属性的简单方法:

(defn bar [x y]
  (let [foo (fn [x] (* x x))]
    foo))

(identical? (class (bar 1 2)) (class (bar 1 2)))
;= true

NB. Clojure足够聪明,可以注意到foo不是“实际闭包”(它关闭了bar的参数,但它实际上并没有使用它们),因此foo的运行时表示不带任何额外的字段,闭包会但是,每次调用bar都会分配一个foo类的新实例.

单独的定义方法:

有一个foo实例,但是调用它涉及到Var的间接,它本身带有非零成本.除了最具性能敏感性的代码之外,这个成本通常不值得担心,但它就在那里,因此将本地功能分解出去可能不一定是性能上的胜利.像往常一样,如果它值得优化,那么首先值得测量/基准测试.

放过lambda

还有丹尼尔提到的最后一个选项,其中释放,而不是内部,定义.使用这种方法,有一个(类)foo的单个实例;它存储在栏内的字段中;它用于所有对foo内部的调用.

网友评论