Почему частичное настолько медленное в 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) является действительно ужасным.