Измерение времени выполнения в будущем в Clojure с использованием «времени» - PullRequest
0 голосов
/ 26 июня 2018

Ниже приведена упрощенная версия приложения, над которым я работаю. В частности, я заинтересован в оценке времени выполнения process-list. В функции process-list я делю входной список на разделы, равные количеству потоков, которые я хотел бы выполнить параллельно. Затем я передаю каждый раздел в поток через вызов future. Наконец, в main я звоню process-list с time, обернутым вокруг него. Время должно возвращать истекшее время обработки, выполненное process-list, но, по-видимому, оно только возвращает количество времени, которое требуется для создания будущих потоков, и не ожидает завершения выполнения фьючерсов. Как я могу разыменовать фьючерсы внутри process-list, чтобы гарантировать, что истекшее время учитывает выполнение будущих потоков до завершения?

(ns listProcessing
  (:require [clojure.string]
            [clojure.pprint]
            [input-random :as input]))

  (def N-THREADS 4)      
  (def element_processing_retries (atom 0))

  (def list-collection
     "Each element is made into a ref"
     (map ref input/myList))

  (defn partition-list  [threads list]
      "partition list into required number of partitions which is equal 
      to the number of threads"
      (let [partitions (partition-all 
         (Math/ceil (/ (count list) threads))  list)]
            partitions))
 (defn increase-element [element]
     (ref-set element inc))

 (defn process-list [list]
      "Process `members of list` one by one."
      (let [sub-lists (partition-list N-THREADS list)]
      (doseq [sub-list sub-lists]
        (let [futures '()
              myFuture        (future (dosync  (swap! element_processing_retries inc)
              (map increase-element sub-list)))]
              (cons myFuture futures) 
              (map deref futures))))) 


  (defn main []      
       (let [f1 (future (time (process-list input/mylist)))]        
        @f1)       
  (main)    
  (shutdown-agents)

Ниже приведен пример упрощенного ввода списка: обратите внимание, что ввод здесь упрощен, и обработка списка также упрощает вопрос.

(ns input-random)
(def myList (list 1 2 4 7 89 12 34 45 56))

1 Ответ

0 голосов
/ 27 июня 2018

Это будет иметь некоторые накладные расходы. Если вы пытаетесь получить time разницы в миллисекундах, это немного искажает вещи (хотя в любом случае минутные интервалы не следует использовать time).

Я думаю, что ваш пример был немного запутанным, поэтому я сократил его до того, что, по моему мнению, представляет проблему немного лучше:

(time (doseq [n (range 5)]
        (future
          (Thread/sleep 2000))))

"Elapsed time: 1.687702 msecs"

Проблема здесь та же, что и с вашим кодом: все, что действительно нужно, это время, которое требуется doseq для отправки всех заданий.

Идея моего взлома состоит в том, чтобы поместить каждое законченное задание в атом, а затем проверить состояние завершения в состоянии ожидания:

(defn do-stuff [n-things]
  (let [ret-atom (atom 0)]
    (doseq [n (range n-things)]
      (future
        (Thread/sleep 2000)
        (swap! ret-atom inc)))

    ret-atom))

; Time how long it takes the entire `let` to run
(time
  (let [n 5
        ret-atom (do-stuff n)]

    ; Will block until the condition is met
    (while (< @ret-atom n))))

"Elapsed time: 2002.813288 msecs"

Причина, по которой это так сложно для времени, заключается в том, что все, что вы делаете, - это ускорение побочных эффектов в doseq. Нет ничего, что определяло бы то, что «сделано», поэтому нечего блокировать. Я не очень хорошо разбираюсь в core.async, но подозреваю, что там может что-то помочь. Может быть возможно иметь вызов к <!!, который блокирует, пока канал не имеет определенного количества элементов. В этом случае вам просто нужно поместить результаты в канал по мере их появления.

...