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

tim*_*day 17 performance multithreading 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)(_+_))
Run Code Online (Sandbox Code Playgroud)

(是的,我知道有多少更有效的方式来总结一系列整数,它只是给了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
Run Code Online (Sandbox Code Playgroud)

令我感到惊讶的是,这并没有真正扩展到超过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...
Run Code Online (Sandbox Code Playgroud)

这要快得多,我必须显着增加迭代次数才能运行任何时间!结果是:

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
Run Code Online (Sandbox Code Playgroud)

这更像我希望看到的(虽然3个工作案例有点奇怪,其中一个任务在其他两个任务之前持续完成几秒钟).

进一步推动,在四核超线程i7上,后者版本JOBS=8实现了x4.4加速比JOBS = 1,CPU使用率为571%.

Rex*_*err 15

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

  • @Elazar - 最终有了专业化,它可以在没有对象创建的情况下运行(或类似的东西).但是现在,它是不可避免的:代码是通用的,所以你必须创建它才能工作的对象,即使它只是一个原语的包装器. (2认同)