Подтвердить что ты не робот

Почему мои фьючерсы scala более эффективны?

Я запускаю этот код scala в 32-разрядной четырехъядерной системе Core2:

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

Я удивлен, что на самом деле это не очень хорошо выходит за рамки двух фьючерсов "в игре". Я использую многопоточный код на С++ и не сомневаюсь, что получим хорошее масштабирование до 4 ядер и увижу > 390% загрузки процессора, если бы я закодировал подобные вещи с помощью Intel TBB или boost::threads (это было бы значительно больше конечно, конечно).

Итак: что происходит и как я могу получить масштабирование до 4 ядер, которые я ожидаю увидеть? Это ограничено чем-то в scala или JVM? Это происходит со мной, я на самом деле не знаю, где "scala запускает фьючерсы... это поток, порожденный в будущем, или" Фьючерсы "предоставляют пул потоков, предназначенный для их запуска?

[Я использую пакеты scala 2.7.7 от Debian/Squeeze в системе Lenny с помощью sun-java6 (6-20-0lennny1).]

Update:

Как было предложено в ответе 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

что намного больше похоже на то, что я надеюсь увидеть (хотя случай с тремя заданиями немного странный, причем одна задача последовательно завершается за пару секунд до двух других).

Нажав его немного дальше, на четырехъядерном гиперпотоке i7 последняя версия с JOBS=8 достигает ускорения x4.4 против JOBS = 1 с использованием процессора на 571%.

4b9b3361

Ответ 1

Я предполагаю, что сборщик мусора делает больше работы, чем сама добавка. Таким образом, вы ограничены тем, чем может управлять сборщик мусора. Попробуйте снова запустить тест с чем-то, что не создает никаких объектов (например, используйте цикл while вместо диапазона/карты/свернуть). Вы также можете играть с параллельными параметрами GC, если ваше реальное приложение сильно ударит по GC.

Ответ 2

Try

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

Приложение view должно (как я понял, id) избегать повторной итерации и создания объекта, предоставляя простые обертки.

Обратите внимание также, что вместо сгиба можно использовать reduceLeft(_+_).