【问题标题】:clojure pmap - why aren't i using all the cores?clojure pmap - 为什么我不使用所有核心?
【发布时间】:2016-08-18 03:44:50
【问题描述】:

我正在尝试使用 clojure pantomime 库从大量 tif 文档(以及其他文档)中提取/ocr 文本。

我的计划是使用 pmap 对一系列输入数据(来自 postgres 数据库)应用映射,然后使用 tika/tesseract OCR 输出更新同一个 postgres 数据库。这一直工作正常,但是我在 htop 中注意到许多内核有时处于空闲状态。

有什么办法可以解决这个问题,我可以采取什么步骤来确定为什么这可能会在某处阻塞?所有处理都发生在一个 tif 文件上,并且每个线程都是完全互斥的。

附加信息:

  1. 一些 tika/tesseract 进程需要 3 秒,而另一些则需要 90 秒。一般来说,tika 受 CPU 限制很大。根据htop,我有足够的可用内存。
  2. postgres 在会话管理中没有锁定问题,所以我认为这不会妨碍我。
  3. 也许future 的某个地方正在等待deref?怎么知道在哪里?

感谢任何提示,谢谢。下面添加的代码。

(defn parse-a-path [{:keys [row_id, file_path]}]
      (try
        (let [
              start        (System/currentTimeMillis)
              mime_type    (pm/mime-type-of file_path)
              file_content (-> file_path (extract/parse) :text)
              language     (pl/detect-language file_content)
              ]
          {:mime_type   mime_type
          :file_content file_content
          :language     language
          :row_id       row_id
          :parse_time_in_seconds   (float (/ ( - (System/currentTimeMillis) start) 100))
          :record_status "doc parsed"})))


(defn fetch-all-batch []
      (t/info (str "Fetching lazy seq. all rows for batch.") )
      (jdbc/query (db-connection)
                  ["select
                   row_id,
                   file_path ,
                   file_extension
                   from the_table" ]))


(defn update-a-row [{:keys [row_id, file_path, file_extension] :as all-keys}]
      (let [parse-out (parse-a-path all-keys )]
        (try
          (doall
            (jdbc/execute!
              (db-connection)
              ["update the_table
               set
               record_last_updated        = current_timestamp ,
               file_content          = ?                 ,
               mime_type             = ?                 ,
               language              = ?                 ,
               parse_time_in_seconds = ?                 ,
               record_status         = ?
               where row_id = ? "
               (:file_content          parse-out) ,
               (:mime_type             parse-out) ,
               (:language              parse-out) ,
               (:parse_time_in_seconds parse-out) ,
               (:record_status         parse-out) ,
               row_id ])
            (t/debug (str "updated row_id " (:row_id parse-out) " (" file_extension ") "
                          " in " (:parse_time_in_seconds parse-out) " seconds." )))
          (catch  Exception _ ))))

(dorun
  (pmap
    #(try
       (update-a-row %)
       (catch Exception e (t/error (.getNextException e)))
       )
    fetch-all-batch )
  )

【问题讨论】:

  • Rasterize 建议 pmap 由于分块序列而“损坏”。 (rasterize.io/blog/clojure-the-good-parts.html)。
  • 请分享您的代码的 sn-p 您如何使用pmapfuture。很难猜测是您犯了错误还是这些工具的限制。
  • 添加代码被剪断,谢谢。

标签: clojure pmap


【解决方案1】:

pmap 在(+ 2 个内核)的批次上并行运行 map 函数,但保留顺序。这意味着如果您有 8 个核心,将处理 10 个项目的批次,但只有在所有 10 个都完成后才会开始新批次。

您可以创建自己的代码,使用futuredelayderef 的组合,这将是很好的学术练习。之后,您可以丢弃您的代码并开始使用 claypoole 库,它有一组抽象,涵盖了 future 的大部分用途。

对于这种特定情况,使用它们的无序 pmappfor 实现(upmapupfor),它们的作用与 pmap 完全相同,但没有排序;批次中的任何一件物品完成后,就会立即拾取新物品。

在 IO 是主要瓶颈的情况下,或者工作项之间的处理时间可能有很大差异的情况下,这是并行化 mapfor 操作的最佳方式。

当然,您应该注意不要依赖任何类型的返回值排序。

  (require '[com.climate.claypoole :as cp])

  (cp/upmap (cp/ncpus)
    #(try
       (update-a-row %)
       (catch Exception e (t/error (.getNextException e)))
       )
    fetch-all-batch )

【讨论】:

  • 这很好用,谢谢。对于其他阅读者,使用 cp/upmap 时需要注意的事情是错误捕获的工作原理、日志记录(以及它如何产生副作用)等。再次感谢。
  • 另外,想添加这个链接,因为我真的很喜欢claypoole:youtube.com/watch?v=BzKjIk0vgzE
【解决方案2】:

前段时间我也遇到过类似的问题。我猜你的假设和我一样:

  • pmap 并行调用 f。但这并不意味着工作是平等分享的。正如你所说,有些需要 3 秒,而另一些需要 90 秒。在 3 秒内完成的线程不会要求其他线程共享一些剩余的工作。所以完成的线程只是等待直到最后一个完成。

  • 您没有准确描述您的数据如何,但我会假设您正在使用某种惰性序列,这不利于并行处理。如果您的进程是 CPU 受限的并且您可以将整个输入保存在内存中,那么更喜欢使用 clojure.core.reducers('map'、'filter' 和特别是 'fold')而不是使用惰性映射、过滤器等。

就我而言,这些技巧将处理时间从 34 秒缩短到了 8 秒。希望对你有帮助

【讨论】:

  • 感谢您的提示。我已经按照你说的做了,把输入的惰性序列拉到内存中传递到 pmap/r/map 中。
  • @joefromct 请注意,r/map 不能并行完成工作!为此,您需要使用用于并行处理的折叠。它是 clojure 的一种“mapreduce”;)
猜你喜欢
  • 2016-09-10
  • 1970-01-01
  • 2018-12-31
  • 1970-01-01
  • 1970-01-01
  • 2014-07-01
  • 2013-03-12
  • 2013-11-26
  • 1970-01-01
相关资源
最近更新 更多