Тюнинг Clojure Редукторы Производительность библиотеки

StackOverflow https://stackoverflow.com//questions/22031829

  •  21-12-2019
  •  | 
  •  

Вопрос

Почему отображение / уменьшение с библиотекой редукторов имеет худшую производительность, чем обычная карта / уменьшить?

user=> (time (reduce + (map inc (range 100000000))))
"Elapsed time: 14412.627966 msecs"
5000000050000000

user=> (time (reduce + (r/map inc (range 100000000))))
... (C-c)

user=> (time (r/reduce + (r/map inc (range 100000000))))
....(C-c)
.

У меня было два убийства позже два, так как требуется бесконечно долго.Что здесь не так?

<Сильные> Редактировать: Похоже, другие языки также имеют подобные проблемы.Скала, кажется, ломается только на миллион. Почему Scala Parallel Collections иногда вызывает OutofMeMoryError? ОтказВ то время как редукторы Clojure быстрее, чем обычно на миллион.

Это было полезно?

Решение

Чтобы дополнить ответ @ A-Webb, это запись компилятора и участие, чтобы действительно исправить.( Смотрите этот пост для более подробной информации.)

Другой способ работы по всей проблеме - использовать A PUSE :

(defn once-seq
  "Returns a sequence on which seq can be called only once."
  [coll]
  (let [a (atom coll)]
    (reify clojure.lang.Seqable
      (seq [_]
        (let [coll @a]
          (reset! a nil)
          (seq coll))))))
.

а затем:

=> (time (r/reduce + (r/map inc (once-seq (range 100000000)))))
"Elapsed time: 3692.765 msecs"
5000000050000000
.

Другие советы

Производительность измельчается до остановки, поскольку память исчерпана.Если вы продолжаете ждать, вы, скорее всего, встретите ошибку памяти.Создание редуктора удерживает голову коллекции в закрытии.Таким образом, огромная ленивая последовательность галстуки вверх память, как это становится реализованным.

Вот что происходит, дистиллировано

user=> (def n 100000000)
#'user/n

user=> (time (dorun (range n)))
"Elapsed time: 12349.034702 msecs"
.

Теперь одинаково, но из-за закрытия

user=> (defn foo [x] (fn [f] (f x)))
#'user/foo

user=> (time ((foo (range n)) dorun))
OutOfMemoryError GC overhead limit exceeded ... (sometime later)
.

Сравнить с

(time (do (doall (range n)) nil))
OutOfMemoryError GC overhead limit exceeded ... (sometime later)
.

Подозреваемое закрытие в редукторах

user=> (source r/folder)
(defn folder
  "Given a foldable collection, [...]"
  {:added "1.5"}
  ([coll xf]
     (reify
      clojure.core.protocols/CollReduce
      (coll-reduce [_ f1]
                   (clojure.core.protocols/coll-reduce coll (xf f1) (f1)))
   ...
.

Christophe Grand имеет хороший пост на том, как составить редукторы в ленивом моде.

Редукторы

не очень хорошо работают с ленивыми списками, в то время как нормальное уменьшение.

Чтобы получить реальную выгоду из редукторов, вам нужна не ленивая коллекция, например,Вектор, и вам нужно использовать складку вместо уменьшения.

 (def v (into [] (range 10000000)))
 (time (reduce + (map inc v)))
 ;; "Elapsed time: 896.79 msecs"
 (time (reduce + (r/map inc v)))
 ;; "Elapsed time: 671.947 msecs" 
 (time (r/fold + (r/map inc v)))
 ;; "Elapsed time: 229.45 msecs"
.

Редукторы работают с рамками вилки / объединения, которые требуют больших кусков данных.В ленивой (оболочке) последовательности у вас нет этих больших кусков, поэтому вилка / присоединение не может работать хорошо.

Есть разговор с Rich Hikkey на редукторы, которые действительно хорошо объясняют концепции: https://vimeo.com/45561411

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top