我在32位四核Core2系统上运行此scala代码:
def job(i:Int,s:Int):Long = {
val r=(i to 500000000 by s).map(_.toLong).foldLeft(0L)(_+_)
println("Job "+i+" done")
r
}
import scala.actors.Future
import scala.actors.Futures._
val JOBS=4
val jobs=(0 until JOBS).toList.map(i=>future {job(i,JOBS)})
println("Running...")
val results=jobs.map(f=>f())
println(results.foldLeft(0L)(_+_))
(是的,我做知道很多更有效的方法来对一系列整数求和;它只是为了给CPU做些事情。)
根据我设置JOBS的内容,代码会在以下时间运行:
JOBS=1 : 31.99user 0.84system 0:28.87elapsed 113%CPU
JOBS=2 : 27.71user 1.12system 0:14.74elapsed 195%CPU
JOBS=3 : 33.19user 0.39system 0:13.02elapsed 257%CPU
JOBS=4 : 49.08user 8.46system 0:22.71elapsed 253%CPU
令我感到惊讶的是,这并没有真正扩展到超过2个未来“在游戏中”。我做了很多多线程C ++代码,毫无疑问我可以很好地扩展到4个内核,并且如果我使用英特尔的TBB或boost::threads
编写这类内容,则可以看到> 390%的CPU利用率当然要多得多了。)
那么:发生了什么,如何扩展到我期望看到的4个核心?这是否受到scala或JVM中某些内容的限制?它发生在我身上我实际上并不知道“scala的期货在哪里运行......是每个未来产生的线程,还是”Futures“提供了一个专门用于运行它们的线程池?
[我在使用sun-java6(6-20-0lennny1)的Lenny系统上使用来自Debian / Squeeze的scala 2.7.7软件包。]
更新
正如Rex的答案中所建议的那样,我重新编码以避免创建对象。
def job(i:Long,s:Long):Long = {
var t=0L
var v=i
while (v<=10000000000L) {
t+=v
v+=s
}
println("Job "+i+" done")
t
}
// Rest as above...
这要快得多,我必须显着增加迭代次数,才能运行任何时间!结果是:
JOBS=1: 28.39user 0.06system 0:29.25elapsed 97%CPU
JOBS=2: 28.46user 0.04system 0:14.95elapsed 190%CPU
JOBS=3: 24.66user 0.06system 0:10.26elapsed 240%CPU
JOBS=4: 28.32user 0.12system 0:07.85elapsed 362%CPU
这更像是我希望看到的(尽管3个工作案例有点奇怪,其中一个任务在其他两个任务之前持续完成几秒钟。)
进一步推动,在四核超线程i7上,后者版本JOBS=8
实现了x4.4加速比JOBS = 1,CPU使用率为571%。
答案 0 :(得分:15)
我的猜测是垃圾收集器比添加本身做的工作更多。因此,您受到垃圾收集器可以管理的限制。尝试使用不会创建任何对象的内容再次运行测试(例如,使用while循环而不是range / map / fold)。如果您的真实应用程序会严重影响GC,您也可以使用并行GC选项。
答案 1 :(得分:2)
尝试
(i to 500000000 by s).view.map(_.toLong).foldLeft(0L)(_+_)
view
的应用程序应该(我理解为id)通过提供简单的包装器来避免重复迭代和对象创建。
另请注意,您可以使用reduceLeft(_+_)
代替折叠。