Clojure core.async用于数据计算

时间:2015-10-19 00:35:26

标签: performance concurrency clojure core.async

我已经开始使用clojure core.async库了。我发现CSP,channel,go blocks的概念非常容易使用。但是,我不确定我是否正确使用它们。我有以下代码 -

(def x-ch (chan))
(def y-ch (chan))
(def w1-ch (chan))
(def w2-ch (chan))

; they all return matrices
(go (>! x-ch (Mat/* x (map #(/ 1.0 %) (max-fold x)))))
(go (>! y-ch (Mat/* y (map #(/ 1.0 %) (max-fold y)))))
(go (>! w1-ch (gen-matrix 200 300)))
(go (>! w2-ch (gen-matrix 300 100)))

(let [x1 (<!! (go (<! x-ch)))
        y1 (<!! (go (<! y-ch)))
        w1 (<!! (go (<! w1-ch)))
        w2 (<!! (go (<! w2-ch)))]

    ;; do stuff w/ x1 y1 w1 w2
)

我在符号xy中有预定义的(矩阵)向量。在使用之前我需要修改两个向量。那些向量非常大。我还需要生成两个随机矩阵。由于go宏异步启动计算,因此我将所有四个计算任务拆分为单独的go块,并将后续结果放入通道中。然后我有一个let块,我从通道中获取值并将它们存储到符号中。他们都使用阻塞<!!接受函数,因为它们位于主线程上。

我正在尝试做的基本上是通过将程序片段分成异步进程来加快我的计算时间。这是正确的方法吗?

3 个答案:

答案 0 :(得分:5)

对于此类处理, future 可能稍微充分。

链接中的示例很容易理解:

 (def f 
   (future 
     (Thread/sleep 10000) 
     (println "done") 
     100))

处理,将来的块立即启动,所以上面确实启动一个线程,等待10秒并在完成后打印“完成”。

当您需要该值时,您可以使用:

(deref f)
; or @f

将阻止并返回未来代码块的值。

在同一示例中,如果在10秒之前调用deref,则调用将阻塞,直到计算完成。

在您的示例中,由于您只是在等待计算完成,并且不太关注通道参与者之间的消息和交互 future 是我建议的。所以:

 (future 
    (Mat/* x (map #(/ 1.0 %) (max-fold x))))

答案 1 :(得分:3)

go块返回带有表达式结果的通道,因此您无需为其结果创建中间通道。下面的代码允许您同时启动所有4个计算,然后阻止值直到它们返回。如果您不需要立即获得某些结果,则只有在实际使用时才能阻止该值。

(let [x1-ch (go (Mat/* x (map #(/ 1.0 %) (max-fold x))))
      y1-ch (go (Mat/* y (map #(/ 1.0 %) (max-fold y))))
      w1-ch (go (gen-matrix 200 300))
      w2-ch (go (gen-matrix 300 100))
      x1 (<!! x1-ch)
      y1 (<!! y1-ch)
      w1 (<!! w1-ch)
      w2 (<!! w2-ch)]
  ;; do stuff w/ x1 y1 w1 w2
  )

答案 2 :(得分:1)

如果您希望通过并行运行代码来更频繁地加速您的程序,那么您可以使用Clojure的Reducers或Aphyr的Tesser来查看。这些工作通过将单个计算的工作分成可并行的部分,然后将它们组合在一起。这些将有效地在您的计算机所拥有的核心上运行工作。如果您使用future或in go块运行每个计算,那么每个计算将在单个线程上运行,有些可能在其他线程之前完成,并且这些核心将处于空闲状态。