Почему частичное настолько медленное в clojure
Сверхбыстрая скорость.
(let [a (atom {})]
(doall (map #(swap! a merge {% 1}) (range 10000))) (println @a))
Но если добавить частичный, то он настолько медленный. Результат возврата кодом должен быть таким же, не так ли? почему производительность сильно отличается?
(let [a (atom {})]
(doall (map #(swap! a (partial merge {% 1})) (range 10000))) (println @a))
Ответы
Ответ 1
(partial f a)
и #(f a %)
на самом деле совсем другие.
Независимо от определения f
, вам разрешено предоставлять любое количество аргументов частично примененной функции, а среда выполнения помещает их в список и использует apply
для получения результата. Итак, независимо от того, у вас есть короткий список, построенный каждый раз при использовании функции, построенной с помощью partial
. С другой стороны, #()
создает новый класс, и если вы используете более старую JVM, которая изолирует константу от обычной кучи, это может стать проблемой, поскольку вы используете все больше и больше выделенной памяти для классов.
Ответ 2
Даже если ответ @noisesmith прав, проблема с производительностью не связана с partial
.
Проблема более тривиальная: это только порядок, в котором параметры передаются на merge
.
В #(swap! a merge {% 1})
атом передается как первый параметр в merge
. На каждом шаге к карте роста атомов присоединяется только {% 1}
.
В #(swap! a (partial merge {% 1}))
атом передается как второй параметр на merge
, и на каждом шаге все элементы атома a
соединены с {% 1}
.
Попробуйте тест с merge'
, который вызывает merge
, изменяя параметры. Карта, на которой объединены все элементы из других карт, является последней:
(defn merge' [& maps]
(apply merge (reverse maps)))
(require '[criterium.core :as c])
(c/quick-bench
(let [a (atom {})]
(dorun (map #(swap! a merge {% 1}) (range 10000))) ))
=> Execution time mean : 4.990763 ms
(c/quick-bench
(let [a (atom {})]
(dorun (map #(swap! a (partial merge' {% 1})) (range 10000))) ))
=> Execution time mean : 7.168238 ms
(c/quick-bench
(let [a (atom {})]
(dorun (map #(swap! a (partial merge {% 1})) (range 10000))) ))
=> Execution time mean : 10.610342 sec
Показатели с merge
и (partial merge')
сопоставимы. (partial merge)
является действительно ужасным.