【问题标题】:Measuring time of a process executing inside a future in Clojure using "time"使用“时间”测量在 Clojure 中执行的进程的时间
【发布时间】:2018-06-27 08:49:43
【问题描述】:

以下是我正在开发的应用程序的简化版本。具体来说,我有兴趣对process-list 的执行时间进行基准测试。在函数process-list 中,我将输入列表划分为等于我希望并行执行的线程数的分区。然后我通过调用future 将每个分区传递给一个线程。最后,在main 中,我调用process-list 并用time 包裹它。 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))

【问题讨论】:

  • 我已经简化了问题
  • 应该注意的是,您的dosync 似乎无限期地被阻止。如果你在它后面加上print,它永远不会打印。我不经常使用sync 来知道为什么会这样,但这肯定会导致问题。
  • 好像是因为(map increase-element element)element 是一个数字,所以我不确定你想在那里做什么。你不能map 超过一个数字。这会默默地失败,因为你吃不饱的未来会吞下错误。
  • "element" 是一个列表。我可能应该重命名它。它代表分区列表中的每个分区
  • 不,elements 是每个分区。 element 是每个分区中的每个数字。我认为您对letdoseq 感到困惑。您可能已经查看代码太久了。

标签: clojure benchmarking future


【解决方案1】:

这会有一些开销。如果您尝试time 毫秒差异,这会有点偏差(尽管分钟计时不应该使用time)。

我认为您的示例有点令人费解,因此我将其简化为我认为更好地代表问题的内容:

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

"Elapsed time: 1.687702 msecs"

这里的问题与您的代码的问题相同:所有这一切都是时间doseq 调度所有作业需要多长时间。

我的 hack 的想法是将每个完成的作业放入一个原子中,然后在忙碌的等待中检查结束条件:

(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 不太满意,但我怀疑那里可能会有一些帮助。有可能调用&lt;!! 会阻塞,直到通道具有一定数量的元素。在这种情况下,您只需在生成结果时将其放入通道中。

【讨论】:

  • 我明白你的意思。我还想知道您是否在 Clojure 中取消了未来,它不等待未来的完全执行吗?在那种情况下,我不会得到未来的全部执行时间吗?看看我当前的编辑,我试图取消创建的期货。我还处理了前面提到的子列表 :) 问题。
  • @Gakuo 它确实在等待未来的完全执行。问题是,既然你回到了基础任务,未来的身体会立即返回。期货不会通过它们的身体寻找其他期货来等待。你给未来的主体只是卡在fn 中,并在线程池中运行。一旦fn返回,future就完成了。
  • @Gakuo 我明天会看看你的更新。我的大脑被炸了。
  • 在这种情况下,请告诉我是否有一种方法可以访问进程列表中在后台运行的那些期货的执行时间。那是明天:)
  • @Gakuo 我认为您的新代码有太多错误,我无法对此发表评论。 (cons myFuture futures) (map deref futures)) 两者都不做。第一种形式conss 将future 放到列表中,但随后您将其丢弃,因为您没有保存它的返回值。后者不做任何事情,因为map 在请求结果之前不会产生结果。和以前一样,你丢弃了它的返回值,所以map 永远不会运行。
猜你喜欢
  • 1970-01-01
  • 2019-10-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-15
  • 2011-04-23
相关资源
最近更新 更多