2014-06-18 3 views
-3

У меня есть вектор карты, что-то, как показано ниже, хотя есть до possbly 100 карт в каждом наборе:струнные сумма значений из вектора отображений Clojure

data({ a:a b:"2" c:t}{ a:b b:"0" c:t}{ a:c b:"-4" c:t}{ a:d b:"100" c:t}{ a:e b:"50" c:t}) 

Мне нужно, чтобы произвести сумму: б

values(map :b data) 
sum(reduce + (map read-string values) 

Это дает желаемый результат, но расчет длится примерно 1/10 в секунду. Я делаю это для нескольких сотен тысяч наборов данных, поэтому для этого требуется много времени на обработку.

Может ли кто-нибудь предложить более эффективный/быстрый способ такого подхода?

Благодаря

+8

Пожалуйста, дайте настоящий код clojure вместо этого псевдокода. Мы можем это принять. – ivant

+0

Это реальный код clojure ... – redhands

+0

Почему бы не хранить int как int, а не строку? Производительность не приходит только из алгоритма, это комбинация структуры + алгоритм – Ankur

ответ

3

Одним из возможных вариантов является использование reducers которые идут параллельно:

(require '[clojure.core.reducers :as r]) 
(r/reduce + (r/map read-string values)) 

Для вашего небольшого теста это не улучшит время выполнения, но для больших наборов данных оно должно быть.

+0

Хотелось бы использовать редукторы, но, к сожалению, я использую более старую версию clojure и не смогу быстро обновиться в ближайшее время. – redhands

+3

Вы можете получить некоторый параллелизм, используя 'pmap'. Он был доступен из clojure 1.0, чтобы вы могли его использовать. – ivant

3

Вы можете попробовать использовать Integer/parseInt или Long/parseLong вместо более общего read-string.

[EDIT]

Простой тест с Clojure 1.5.1 показывает, что ParseInt примерно в 10 раз быстрее:

user=> (time (dotimes [n 100000] (read-string "10"))) 
"Elapsed time: 142.516849 msecs" 
nil 

user=> (time (dotimes [n 100000] (Integer/parseInt "10"))) 
"Elapsed time: 12.754187 msecs" 
nil 
+0

Я пробовал это (уменьшить + (map # (Integer/parseint% 1) значения)), но это не имеет значения – redhands

5

Вот 1/10 из всего вашего 100.000 наборов данных, выполненных чуть более 1/10 секунды, на Clojure 1.2.1. Это в основном ваш код (который на самом деле не является истинным синтаксисом clojure, но мы получаем суть), но как-то работает на скорости 10.000x.

;generate 10.000 datasets of 100 maps having 10 fields each 

(def scenario-data 
    (vec (repeatedly 10000 
        (fn [] (vec (repeatedly 100 (fn [] (zipmap 
                  [:a :b :c :d :e :f :g :h :i :j] 
                  (repeatedly (fn [] (str (- (rand-int 2000) 1000)))))))))))) 


;now map the datasets into the reduced sums of the parsed :b fields of each dataset 

(time (doall (map (fn [dataset] (reduce (fn [acc mp] (+ acc (Integer/parseInt (:b mp)))) 0 dataset)) 
        scenario-data))) 
"Elapsed time: 120.43267 msecs" 
=> (2248 -6383 7890 ...) 

Поскольку этот сценарий является довольно большой объем памяти (10000 наборов данных ~ = 600MB, общий расчет использует ~ 4 Гб), я не могу запустить сценарий 100,000 набора данных на моей домашней машине. Тем не менее, я могу запустить его, если я не держу наборы данных в памяти, но карта более ленивой последовательность без проведения на его голову ..

(time (doall (map (fn [dataset] (reduce (fn [acc mp] (+ acc (Integer/parseInt (:b mp)))) 0 dataset)) 
        (repeatedly 100000 
           (fn [] (repeatedly 100 (fn [] (zipmap 
                   [:a :b :c :d :e :f :g :h :i :j] 
                   (repeatedly (fn [] (str (- (rand-int 2000) 1000)))))))))))) 
"Elapsed time: 30242.371308 msecs" 
=> (-4975 -843 1560 ...) 

это 30 секунд для расчета вашего 100.000 версии набора данных и включает все время, необходимое для генерации данных. Использование pmap сокращает это время примерно наполовину (4 ядра).

Редактировать: Создание полностью реализованных 100 000 наборов данных на машине с достаточной памятью занимает 135 секунд. Запуск кода суммирования по нему занимает ~ 1500 мс. Использование pmap сокращает это до ~ 750 мс. A read-string версия ~ 3.5x медленнее.

TL/DR: алгоритм, который вы опубликовали, можно запустить по сценарию 100 000 наборов данных за 1 секунду, учитывая достаточную память.

Пожалуйста, разместите свой полный код, в том числе о том, как вы читаете и сохраняете наборы данных в памяти, и убедитесь, что на этот раз как синтаксис, так и наблюдения являются точными. Вероятно, это скорее проблема памяти из-за того, что вы не читаете эти данные лениво из источника.

Смежные вопросы