我刚刚开始使用 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
,我们可以消除 addWorker
和 getWorker
定义,而不会降低清晰度、模块化或效率(实际上在这样的情况下这里不需要索引查找,结果将按顺序使用,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/