【问题标题】:Clojure Parallel Mapping and Infinite SequencesClojure 并行映射和无限序列
【发布时间】:2010-08-12 03:39:36
【问题描述】:

假设我用以下方式定义所有自然数的序列:

(def naturals (iterate inc 0))

我还定义了一个将 naturals 映射到 nil 的函数,它需要一些时间来计算,如下所示:

(defn hard-comp [_] (Thread/sleep 500))

注意评估以下 s 表达式的计算时间,由 clojure.core/time 测量。

(dorun (map hard-comp (range 30))) ;15010.367496 毫秒

(dorun (pmap hard-comp (range 30))) ;537.044554 毫秒

(dorun (map hard-comp (doall (take 30 naturals))))) ;15009.488499 毫秒

(dorun (pmap hard-comp (doall (take 30 naturals)))) ; 3004.499013 毫秒

(doall (take 30 naturals)) ; 0.385724 毫秒

(range 30) ; 0.159374 毫秒

pmap 在使用显式范围调用时比使用自然部分快约 6 倍。

由于(= (range 30) (take 30 naturals))返回true并且两个对象都是clojure.lang.LazySeq类型,并且clojure在调用函数之前评估函数的所有参数,如何解释上述时间细节?

【问题讨论】:

    标签: performance optimization map clojure parallel-processing


    【解决方案1】:

    我猜是因为这个:

    user> (chunked-seq? (seq (range 30)))
    true
    user> (chunked-seq? (seq (take 30 naturals)))
    false
    user> (class (next (range 30)))
    clojure.lang.ChunkedCons
    user> (class (next (take 30 naturals)))
    clojure.lang.Cons
    

    试试这个:

    user> (defn hard-comp [x] (println x) (Thread/sleep 500))
    #'user/hard-comp
    user> (time (dorun (pmap hard-comp (range 100))))
    

    请注意,它一次跳转 32 个项目。这就是一个范围内每个块抓取多少个元素。 Chunked seq 会提前预先评估一堆项目以提高性能。在这种情况下,看起来pmap 会在您尝试从范围中抓取一个元素时立即生成 32 个线程。

    您总是可以将自然值填充到向量中以获得分块行为。

    user> (time (dorun (pmap hard-comp (range 100))))
    "Elapsed time: 2004.680192 msecs"
    user> (time (dorun (pmap hard-comp (vec (take 100 naturals)))))
    "Elapsed time: 2005.887754 msecs"
    

    (请注意,时间约为 4 x 500 毫秒,4 是达到 100 需要多少个 32 块。)

    另一方面,您可能不希望分块行为。一次32个线程很多。请参阅this question 了解如何取消分块的示例。

    【讨论】:

      猜你喜欢
      • 2023-04-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-08-02
      相关资源
      最近更新 更多