为什么我的scala期货不是更有效率?

时间:2010-09-02 13:06:59

标签: multithreading performance scala scalability future

我在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%。

2 个答案:

答案 0 :(得分:15)

我的猜测是垃圾收集器比添加本身做的工作更多。因此,您受到垃圾收集器可以管理的限制。尝试使用不会创建任何对象的内容再次运行测试(例如,使用while循环而不是range / map / fold)。如果您的真实应用程序会严重影响GC,您也可以使用并行GC选项。

答案 1 :(得分:2)

尝试

(i to 500000000 by s).view.map(_.toLong).foldLeft(0L)(_+_)

view的应用程序应该(我理解为id)通过提供简单的包装器来避免重复迭代和对象创建。

另请注意,您可以使用reduceLeft(_+_)代替折叠。