import java.util.concurrent.{Executors, TimeUnit}
import scala.annotation.tailrec
import scala.concurrent.{Await, ExecutionContext, Future}
import scala.util.{Failure, Success}
object Fact extends App {
def time[R](block: => R): Long = {
val t0 = System.nanoTime()
val result = block // call-by-name
val t1 = System.nanoTime()
val t: Long = TimeUnit.SECONDS.convert((t1 - t0), TimeUnit.NANOSECONDS)
//println(
// "Time taken seconds: " + t)
t
}
def factorial(n: BigInt): BigInt = {
@tailrec
def process(n: BigInt, acc: BigInt): BigInt = {
//println(acc)
if (n <= 0) acc
else process(n - 1, n * acc)
}
process(n, 1)
}
implicit val ec =
ExecutionContext.fromExecutor(Executors.newFixedThreadPool(2))
val f1: Future[Stream[Long]] =
Future.sequence(
(1 to 50).toStream.map(x => Future { time(factorial(100000)) }))
f1.onComplete {
case Success(s) => {
println("Success : " + s.foldLeft(0L)(_ + _) + " seconds!")
}
case Failure(f) => println("Fails " + f)
}
import scala.concurrent.duration._
Await.ready(Future { 10 }, 10000 minutes)
}
我有上面的Factorial代码,需要使用多个内核来更快地完成程序,并且应该使用更多内核。
所以我改变了,
Executors.newFixedThreadPool(1)使用1个核心
Executors.newFixedThreadPool(2)使用2个核心
更改为 1核心时,结果会显示在 127秒中。 但是当更改为 2个核心时,我会 157秒。
我的疑问是,当我增加核心(并行性)时,它应该会提供良好的性能。但事实并非如此。为什么?
请纠正我,如果我错了或遗失了什么。
先谢谢。
答案 0 :(得分:2)
你是如何衡量时间的?打印出来的结果不是执行所花费的时间,而是每次调用的时间总和。
在REPL中运行Fact.time(Fact.main(Array.empty))
我分别得到90和178两个和一个线程。似乎有意义......
答案 1 :(得分:1)
首先,Dima是对的,你打印的是所有任务的总执行时间,而不是直到最后一个任务完成的总时间。不同之处在于,第一个并行完成所有工作的时间,只有后者显示多线程实际加速。
然而,还有另一个重要的影响。当我使用1,2和3个线程运行此代码并测量总时间(f1
准备好的时间)和总并行时间(您打印的那个)时,我得到以下数据(我也减少了数量)计算从50到20加速我的测试):
1 - 70 - 70
2 - 47 - 94
3 - 43 - 126
乍一看它看起来很好,因为平行时间除以实际时间与线程数大致相同。但是如果你仔细观察一下,你可能会注意到,从1个线程到2个线程的速度只有1.5倍左右,第三个线程只有1.1倍。此外,这些数字表示添加线程时所有任务的总时间实际上都会增加。这可能看起来令人费解。
这个难题的答案是你的计算实际上不受CPU约束。问题是答案(factorial(100000)
)实际上是一个非常大的数字。事实上,它是如此之大,以至于需要大约185KB的内存来存储它。这意味着在计算的后期阶段,factorial
方法实际上变得比CPU绑定更多的内存限制,因为这个大小足以溢出最快的CPU缓存。这就是为什么添加更多线程会减慢每个计算的原因:是的,你的计算速度更快但内存却没有更快。因此,当您使CPU缓存和内存通道饱和时,添加更多线程(内核)并不能提高性能。