【问题标题】:Understanding Clojure Transducer Performance了解 Clojure 传感器性能
【发布时间】: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-nestednested 快?)这一切或部分可能只是基准测试或随机性的产物吗? (我已经为每一个运行了quick-bench 多次,结果相同。)我想知道它是否与into 调用transduce 有关,而在通道版本中根本没有以相同的方式实现.转换器为跨向量或通道应用变换提供了相同的接口,但应用变换的方式不同;这种差异使一切变得不同。

【问题讨论】:

    标签: performance clojure channels transducer


    【解决方案1】:

    关于你的方法的一些评论:

    • 缓冲区大小为 100 万的通道非常罕见。我不希望从这种用法中得出的基准对现实世界的程序有很大的适用性。只需使用 1 的缓冲区大小。这足以让此应用程序成功,并且更接近真实世界的使用情况。
    • 你不需要选择这么大的n。如果您的函数运行得更快,则 criterium 可以获取更多样本,从而更准确地估计其平均时间。 n=100 就足够了。

    进行这些更改后,这是我看到的基准数据:

    Evaluation count : 14688 in 6 samples of 2448 calls.
                 Execution time mean : 39.978735 µs
        Execution time std-deviation : 1.238587 µs
       Execution time lower quantile : 38.870558 µs ( 2.5%)
       Execution time upper quantile : 41.779784 µs (97.5%)
                       Overhead used : 10.162171 ns
    Evaluation count : 20094 in 6 samples of 3349 calls.
                 Execution time mean : 30.557295 µs
        Execution time std-deviation : 562.641738 ns
       Execution time lower quantile : 29.936152 µs ( 2.5%)
       Execution time upper quantile : 31.330094 µs (97.5%)
                       Overhead used : 10.162171 ns
    Evaluation count : 762 in 6 samples of 127 calls.
                 Execution time mean : 740.642963 µs
        Execution time std-deviation : 176.879454 µs
       Execution time lower quantile : 515.588780 µs ( 2.5%)
       Execution time upper quantile : 949.109898 µs (97.5%)
                       Overhead used : 10.162171 ns
    
    Found 2 outliers in 6 samples (33.3333 %)
        low-severe   1 (16.6667 %)
        low-mild     1 (16.6667 %)
     Variance from outliers : 64.6374 % Variance is severely inflated by outliers
    Evaluation count : 816 in 6 samples of 136 calls.
                 Execution time mean : 748.782942 µs
        Execution time std-deviation : 7.157018 µs
       Execution time lower quantile : 740.139618 µs ( 2.5%)
       Execution time upper quantile : 756.102312 µs (97.5%)
                       Overhead used : 10.162171 ns
    

    关键要点是:

    1. 异步开销支配实际处理时间。两个频道版本都比非频道版本慢得多,所以我们再也不用担心“为什么把整个东西放到一个频道上然后再取下来会更快”。
    2. chan-then-nestedchan-xf 之间的差异比您的版本小得多。 chan-xf 仍然慢了一点,但很容易在一个标准差内:这并不是一个了不起的结果。

    【讨论】:

      猜你喜欢
      • 2015-11-06
      • 1970-01-01
      • 2015-03-31
      • 2018-07-02
      • 1970-01-01
      • 1970-01-01
      • 2013-06-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多