multithreading - Clojure:在多个 future 运行计算并不能加快我的程序

标签 multithreading clojure future

我刚刚开始使用 Clojure 并编写了以下代码,用于通过蒙特卡洛模拟估算圆周率。我基本上想创建 X 个线程,每个线程计算落在单位圆中的随机点的数量并返回它。然后我的主线程将它们全部加起来并计算 Pi。

但是,在同一个线程中运行所有样本比通过 futures 在多个线程中拆分计算要快。为什么?

(defn sumFutures [workerFutures acc i]
  (if (= -1 i)
    acc
    (recur workerFutures (+ acc @(nth workerFutures i)) (dec i))))

(defn getResults [workerFutures numSamples]
  (* 4.0 (/ (sumFutures workerFutures 0 (dec (count workerFutures))) numSamples)))

(defn isInCircle []
  (let [x (rand) y (rand)]
    (if (<= (+ (* x x) (* y y)) 1)
      1
      0)))

(defn countInCircle [remaining acc]
  (if (zero? remaining)
    acc
    (recur (dec remaining) (+ acc (isInCircle)))))

(defn getWorker [samplesPerWorker]
  (future
    (countInCircle samplesPerWorker 0)))

(defn addWorker [workers samplesPerWorker]
  (conj workers (getWorker samplesPerWorker)))

(defn getWorkers [workers samplesPerWorker remWorkers]
  (if (not (zero? remWorkers))
    (recur (addWorker workers samplesPerWorker) samplesPerWorker (dec remWorkers))
    (doall workers)))

(defn main [numSamples numWorkers]
  (getResults (getWorkers [] (quot numSamples numWorkers) numWorkers) numSamples))

;; Run all in 1 thread
(main 1000000 1)

;; Split among 100 futures (at least 8 threads)
;; SLOWER 
(main 1000000 100)

基于调试结果的其他几个注意事项:

  • 正在创建正确数量的 future

  • 每个 future 都在计算正确的模拟次数

  • 这是在多线程和处理器内核上运行

最佳答案

如果您有惯用的代码,我认为这会更容易使用。

sumFutures 有效地重新实现了 Clojure 的 + 定义,直接使用递归而不是 Clojures 优化的 reduce 实现。这是一个更简单(并且可能更快)的替代定义:

(defn sum-futures
  [workers]
  (apply + (map deref workers)))

getResults 现在更易于阅读 - 我发现了一个发生有理除法的地方 - 如果我们不需要有理数,将一个操作数设为 double 将节省大量工作。

(defn get-results
  [workers num-samples]
  (* 4.0 (/ (sum-futures workers)
            (double num-samples))))

countInCircle 也可以使用 clojure 的 + 更清楚地表示。

(defn count-in-circle
  [n]
  (apply + (repeatedly n isInCircle)))

getWorkers 再次执行原始递归工作,而不是使用 Clojure 的抽象。如果我们使用 repeatedly,我们可以消除 addWorkergetWorker 定义,而不会降低清晰度、模块化或效率(实际上在这样的情况下这里不需要索引查找,结果将按顺序使用,lazy-seq 版本应该比 vector 表现更好。现在这也是与 sum-futures 一起重构的候选者> 到更高效的基于换能器的版本)。

(defn get-workers
  [num-workers samples-per-worker]
  (repeatedly num-workers
              #(future (count-in-circle samples-per-worker))

最后 main 变成:

(defn main
  [num-samples num-workers]
  (get-results (get-workers (quot num-samples num-workers)
                            num-workers)
               num-workers))

关于multithreading - Clojure:在多个 future 运行计算并不能加快我的程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30007036/

相关文章:

python - 线程池执行器 : how to limit the queue maxsize?

clojure - 编译器异常 java.lang.RuntimeException : Unable to resolve symbol: var in this context

listview - Flutter 仅按需填充 ExpansionTile 数据(一个 Future 对象),而不是在初始化时

java - 循环屏障再利用?

android - 可以从工作线程调用 NotificationManager.notify() 吗?

clojure - 如何从 Clojure 中的嵌套列表或向量中按类型删除项目?

flutter 错误: The argument type 'Future<Object?> Function()' can't be assigned to the parameter type 'FutureOr<dynamic> Function(dynamic)'

error-handling - 使用 future 时 Either 是否有更符合人体工程学的语法?

java - Hibernate 上的多线程 'session.flush'

emacs - 使用 nrepl.el 时如何更新 Clojure 依赖项?