В порядке высказал гипотезу влияния на относительные результаты:
Ваш do-with
функция использует nth
для доступа отдельных элементов во входных коллекциях. nth
работает в линейном режиме по диапазонам, делая do-with
квадратным. Излишне говорить, что это убьет производительность в больших коллекциях.
range
производит секционные секции и map
обрабатывает их чрезвычайно эффективно. (По сути, он создает фрагменты длиной до 32 элементов - на самом деле это будет ровно 32 - путем выполнения жесткой петли над внутренним массивом каждого входного блока по очереди, помещая результаты в внутренние массивы выходных блоков.)
Бенчмаркинг с time
не дает вам устойчивого состояния. (Вот почему один должен действительно использовать правильную библиотеку бенчмаркинга, в случае Clojure, Criterium является стандартным решением.)
Кстати, (map #(/ %1 %2) xs ys)
просто можно записать в виде (map/xs ys)
.
Update:
Я протестированные версии map
, оригинальный do-with
и новая версия do-with
с критериум, используя (range 1 1000)
как оба входа в каждом конкретном случае (как и в тексте вопроса), получая следующее Среднее время выполнения:
;;; (range 1 1000)
new do-with 170.383334 µs
(doall (map ...)) 230.756753 µs
original do-with 15.624444 ms
кроме того, я повторил тест с использованием вектора, хранящийся в Var в качестве входных данных, а не диапазонов (то есть с (def r (vec (range 1 1000)))
на запуске d с использованием r
как обоих аргументов в каждом эталоне). Неудивительно, что оригинал do-with
вошел в первую очередь - nth
очень быстр на векторах (плюс использование nth
с вектором позволяет избежать всех промежуточных распределений, участвующих в пересечении seq).
;;; (vec (range 1 1000))
original do-with 73.975419 µs
new do-with 87.399952 µs
(doall (map ...)) 153.493128 µs
Вот новая do-with
с линейной временной сложностью:
(defn do-with [f xs ys]
(loop [xs (seq xs)
ys (seq ys)
ret (transient [])]
(if (and xs ys)
(recur (next xs)
(next ys)
(conj! ret (f (first xs) (first ys))))
(persistent! ret))))
Спасибо за подробный и хорошо округленный ответ. Похоже, мне нужно больше знать, какой тип операции я делаю для какой структуры данных. – Core