2

像 clojure 一样思考函数式,这对 JVM 来说更好、性能更高、负担更轻

(defn- on-message
  ([options ch {:keys [headers delivery-tag]} ^bytes payload ^CompanyProto$Company$Builder company]
   (check-id company)
   (save company options)
   (basic/ack ch delivery-tag))
  ([options ch ^PersistentHashMap kwargs ^bytes payload]
   (on-message options
               ch
               kwargs
               payload
               (-> (CompanyProto$Company/newBuilder)
                   (.mergeFrom payload)))))

或者

(defn- on-message [options ch {:keys [headers delivery-tag] ^bytes payload}]
  (let [company (-> (CompanyProto$Company/newBuilder) (.mergeFrom payload))]
    (check-id company)
    (save company options)
    (basic/ack ch delivery-tag)))
4

2 回答 2

1

如果您使用 Java 互操作进行开发,请设置*warn-on-reflection*true. 如果有警告,您需要使用类型提示来消除它。然后,您将获得“性能良好且对 JVM 来说不那么繁重”的代码。

PS 具有可变性、简单 JVM 类型和未经检查的溢出的数值计算是另一回事,如果是您的情况,您也需要检查一下。

于 2017-08-17T08:16:11.797 回答
0

您的直接问题的答案是,函数调用中没有引入开销,仅仅是因为该函数恰好有多个参数。在 Clojure 中,所有的函数调用都被编译成一个方法调用。调用的确切方法取决于参数的数量——就 JVM 而言,每个参数都被编译为不同的方法。在编译时确定调用哪个方法,因此在运行时没有开销。

话虽如此,进行嵌套函数调用还是有一些开销。具体来说,每次调用都会使调用堆栈增长一个常数,直到该调用返回。但是,这种开销很小,在这种情况下不太可能对性能产生可衡量的影响。

@ToniVanhala 我先用 let 做了,但一个朋友说 let 不好而且没有功能(他是一个 erlang 开发人员),我是功能语言的新手......他说的另一件事是 clojure 很奇怪不是无堆栈的

这似乎是真正的问题。所以值得解决这个问题。

let只是一个允许我们将值绑定到变量的表达式。这些绑定是不可变的。此外,众所周知,它let可以实现为 lambda 上的宏抽象(或者,在 Clojure 的词汇表中,fn)。所以肯定没有什么let让它“不起作用”。

Clojure 不是无堆栈的也没有什么“奇怪”或令人费解的地方。Clojure 是一种 JVM 语言,调用堆栈深深嵌入在 JVM 的抽象计算模型中。虽然有一些方法可以解决这个问题(例如,延续传递风格),但这些方法要么需要放弃深度 JVM 互操作和/或支付性能损失。Clojure 首先是一种实用的语言,这是一种实用的让步。

此外,Clojure(作为 Lisp)实际上是一个语言框架,您可以使用它来打造您想要的语言。您的语言可能会做出不同的权衡。例如,您的语言可能是"stackless"。或者它可能有一流的延续(免责声明:我是 的作者pulley.cps)。或者它可能有一个完全不同的范式(即逻辑与功能)。但它也可以非常简单。使用 Clojure,您可以选择支付的费用。

具有讽刺意味的是,您的朋友对代码不起作用的说法是正确的。然而,这并不是因为let. 相反,除了 之外的几乎所有东西都let不起作用。例如,(save company options)显然是一个副作用操作。不过,这并不一定意味着您的代码不好——即使是最纯粹的函数式程序,如果要具有任何实用价值,也必须在某些时候与现实世界进行交互。

于 2017-08-19T03:55:19.553 回答