Clojure结构多个计算/写入并行工作

时间:2016-02-18 10:39:05

标签: clojure parallel-processing

假设我有以下代码:

(defn multiple-writes []
  (doseq [[x y] (map list [1 2] [3 4])] ;; let's imagine those are paths to files
     (when-not (exists? x y) ;; could be left off, I feel it is faster to check before overwriting
       (write-to-disk! (do-something x y)))))

我这样称呼(省略参数):

   (go (multiple-writes))

我使用go在后​​台执行某些代码,但我不知道我是否在使用正确的工具。有关这些功能的更多信息:

  • 这不是高优先级的代码 。它甚至可能失败 - multiple-writes可被视为缓存填充函数。
  • 因此我不关心返回值。
  • do-something需要100到500毫秒,具体取决于输入
  • do-something消耗一些内存(使用图像缓冲区,一些图像可以是2000px * 2000px)
  • 每次调用multiple-writes时,都要处理10到40个元素/图像。
  • 每次调用write-to-disk都会创建一个新文件(如果有的话会覆盖它,但不应该发生)
  • write-to-disk始终写在同一目录中

所以我想通过并行执行(write-to-disk! (do-something x y))来加快速度,尽可能快。 但是我根本不想让系统过载,因为是一项高优先级的任务。

我应该怎么做?

注意:尽管有标题,但这不是this question的重复,因为我不想限制为3个帖子(不是说答案不一样,但我觉得这个问题不同)。

2 个答案:

答案 0 :(得分:1)

请考虑将您的设计基于streamsfork/join

我会做一个IO组件。然后,每个处理节点都可以将结果发送到那里保存。这很容易用流模型化。使用fork / join,可以通过不在层次结构中返回结果但将其发送到例如来实现。代理人。

如果内存消耗是一个问题,也许你可以分工更多。像100x100补丁一样。

答案 1 :(得分:1)

看看claypoole library,它提供了一些好的和简单的抽象填充pmap和fork / join reducers之间的空白,否则需要手动编码futurepromise s。

使用pmap并行批处理的所有结果都需要在执行下一批处理之前返回,因为保留了返回顺序。这可能是处理时间变化很大的问题(无论是计算,http请求还是不同“大小”的工作项)。这通常会使pmap减慢到单线程map +不需要的开销性能。

使用claypoole的无序pmap并且(upmapupfor)无序,一个线程(核心)中较慢的函数调用可以被另一个线程上的较快函数调用所取代,因为排序不需要是保留,只要并非所有核心都被慢速呼叫堵塞。

如果IO到一个磁盘是唯一的瓶颈,这可能没有多大帮助,但由于claypoole具有可配置的线程池大小和功能来检测可用内核的数量,因此它将有助于限制内核数量。

fork / join reducers可以通过工作窃取来优化CPU使用率,这可能会大大增加内存使用量,因为没有选项可以在不改变reducer库的情况下限制并行进程的数量。