gpt4 book ai didi

performance - 了解 Clojure 转换器性能

转载 作者:行者123 更新时间:2023-12-03 16:47:03 26 4
gpt4 key购买 nike

在较高的层次上,我理解使用转换器不会创建任何中间数据结构,而通过 ->> 产生一长串操作。确实如此,因此换能器方法的性能更高。这在我下面的一个例子中被证明是正确的。但是,当我添加 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

)
最后有两个结果我不明白。首先,为什么使用带有 channel 的传感器比从 channel 读取所有内容然后进行嵌套映射慢?看起来,使用带有 channel 的换能器的“开销”或其他任何东西要慢得多,以至于它压倒了不创建中间数据结构的 yield 。其次,这个真的出乎意料,为什么把数据放到一个 channel 上然后取下来然后使用嵌套映射技术比不做 channel 舞只使用嵌套映射技术更快? (说得更短,为什么 chan-then-nestednested 快?)这一切可能只是基准测试或随机性的产物吗? (我已经为每个这些都运行了几次 quick-bench,结果相同。)我想知道它是否与 into 有关。调用 transduce而在 channel 版本中根本没有以相同的方式实现。转换器提供了相同的界面来应用跨向量或 channel 的转换,但应用转换的方式不同;而这种差异决定了一切。

最佳答案

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

  • 拥有一个缓冲区大小为 100 万的 channel 是非常不寻常的。我不希望从这种用法得出的基准对现实世界的程序有很大的适用性。只需使用大小为 1 的缓冲区。这足以让此应用程序成功,并且更接近真实世界的使用情况。
  • 没必要挑这么大的n .如果您的函数运行得更快,则标准可以采集更多样本,从而更准确地估计其平均时间。 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
    关键要点是:
  • 异步开销在实际处理时间中占主导地位。两个 channel 版本都比非 channel 版本慢得多,所以我们不再需要担心“为什么把整个东西放到一个 channel 然后再取下来会更快”。
  • chan-then-nested的区别和 chan-xf比你的版本小得多。 chan-xf仍然有点慢,但很容易在一个标准偏差内:并不是一个了不起的结果。
  • 关于performance - 了解 Clojure 转换器性能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65757419/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com