【发布时间】:2021-04-21 17:47:25
【问题描述】:
在高层次上,我理解使用转换器不会创建任何中间数据结构,而通过->> 进行的一长串操作会这样做,因此转换器方法的性能更高。这在我下面的一个例子中被证明是正确的。但是,当我将clojure.core.async/chan 添加到组合中时,我并没有得到我期望的相同的性能改进。显然有一些我不明白的地方,我希望得到解释。
(ns dev
(:require [clojure.core.async :as async]
[criterium.core :as crit]))
;; Setup some toy data.
(def n 1e6)
(def data (repeat n "1"))
;; Reusable thread-last operation (the "slower" one).
(defn tx [x]
(->> x
(map #(Integer. %))
(map inc) (map inc) (map inc) (map inc) (map inc) (map inc)
(map inc) (map inc) (map inc) (map inc) (map inc)))
;; Reusable transducer (the "faster" one).
(def xf (comp
(map #(Integer. %))
(map inc) (map inc) (map inc) (map inc) (map inc) (map inc)
(map inc) (map inc) (map inc) (map inc) (map inc)))
;; For these first two I expect the second to be faster and it is.
(defn nested []
(last (tx data)))
(defn into-xf []
(last (into [] xf data)))
;; For the next two I again expect the second to be faster but it is NOT.
(defn chan-then-nested []
(let [c (async/chan n)]
(async/onto-chan! c data)
(->> c
(async/into [])
async/<!!
tx
last)))
(defn chan-xf []
(let [c (async/chan n xf)]
(async/onto-chan! c data)
(->> c
(async/into [])
async/<!!
last)))
(comment
(crit/quick-bench (nested)) ; 1787.672 ms
(crit/quick-bench (into-xf)) ; 822.8626 ms
(crit/quick-bench (chan-then-nested)) ; 1535.628 ms
(crit/quick-bench (chan-xf)) ; 2072.626 ms
;; Expected ranking fastest to slowest
;; into-xf
;; nested
;; chan-xf
;; chan-then-nested
;; Actual ranking fastest to slowest
;; into-xf
;; chan-then-nested
;; nested
;; chan-xf
)
最后有两个我不明白的结果。首先,为什么使用带有通道的传感器比读取通道中的所有内容然后执行嵌套映射要慢?似乎使用带有通道的转换器的“开销”或其他任何东西都慢得多,以至于它压倒了不创建中间数据结构的收益。其次,这真是出乎意料,为什么将数据放入通道然后将其取出并然后使用嵌套映射技术比不进行通道跳舞更快使用嵌套地图技术? (说得更短,为什么chan-then-nested 比nested 快?)这一切或部分可能只是基准测试或随机性的产物吗? (我已经为每一个运行了quick-bench 多次,结果相同。)我想知道它是否与into 调用transduce 有关,而在通道版本中根本没有以相同的方式实现.转换器为跨向量或通道应用变换提供了相同的接口,但应用变换的方式不同;这种差异使一切变得不同。
【问题讨论】:
标签: performance clojure channels transducer