LINUX.ORG.RU

scala Parallel Collections и таинственный метод ForkJoinPool.scan, который жрёт процессорное время

 ,


0

2

Прогнали пару тестов шуточных и несерьезных методов par scala и asParallel в c#. Результаты получились предсказуемые, но не о них сейчас. Вот код:

trait Calculation[T,R]{
  def before():Unit
  def calculation(v:T):R
  def after():Unit

  def start(t:T){
    before()
    val t1s = System.currentTimeMillis()
    calculation(t)
    val t2s = System.currentTimeMillis()
    println(t2s-t1s)
    System.gc
    after()
  }
}

object Calculation{
  def apply[T,R]( _before: =>Unit,
                 _calculation: (T)=>R,
                 _after: =>Unit
                ):Calculation[T,R]=
  new Calculation[T,R] {
    def calculation(v: T): R = _calculation(v)

    def after(): Unit = _after

    def before(): Unit = _before
  }
}

object Test extends App {

  def cpuTimeEater(num:Int)=Math.pow(num,num)

  val slt = (0 to 5000000).toArray

  val singleClaculation = Calculation(
      {println("Single:")},
      (x:Array[Int])=>{x map cpuTimeEater},
      {println("------------------")}
      )

  val multiCalculation = Calculation(
      {println("Parrallel:")},
      (x:Array[Int])=>{x.par map cpuTimeEater},
      {println("+++++++++++++++++")}
      )

  for(i<-(0 to 10)){
    singleClaculation.start(slt)
    multiCalculation.start(slt)
  }
}

Масшабируется на отлично, но можно и лучше, наверное. Смотрим профилировщиком VisualVM(добавить scala.* в непрофилируемые совершенно забыли), а там:

[Method]scala.concurrent.forkjoin.ForkJoinPool.scan(scala.concurrent.forkjoin.ForkJoinPool.WorkQueue)
[SelfTime %]46.511116	
[SelfTime ]53 450 ms (46,5%)	
[Invocations]122

Гугление выдало вот это с stackoverflow http://stackoverflow.com/questions/19147077/akka-during-load-testing-forkjoin...

Ну и собственно тут подробный коментарий, что оно таки делает. https://github.com/scala/scala/blob/master/src/forkjoin/scala/concurrent/fork...

Но что он так много отжирает то и можно ли на это как-то повлиять?

★★★★★

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

Если я прав, то
Следствие 1: повлиять не выйдет.
Следствие 2: при увеличении нагрузки жрать будет не сильно больше.

ya-betmen ★★★★★ ()
Ответ на: комментарий от ya-betmen

Следствие 2: при увеличении нагрузки жрать будет не сильно больше.

я тут поразмышлял над описанием и думаю что нагрузка упадет, если задачи сделать равноценными и/или более многоядерную машину купить.

Или уменьшить количество воркеров, но как?

RedPossum ★★★★★ ()
Ответ на: комментарий от RedPossum

Конфига тестовой среды нет, результатов работы профилятора нет, скалу одним глазом смотрел год назад и постарался забыть как страшный сон. Помог чем смог.

ya-betmen ★★★★★ ()
Ответ на: комментарий от ya-betmen

И на этом спасибо, пойду копать сам, потому как то ли я что-то делаю в корне не так, то ли вопрос неправильный. Но гугл крайне скуден на эту тему.

RedPossum ★★★★★ ()

А, ну я и пряник. Все дело же в этом:

multiCalculation.start(slt)
//тут все отработало и начинаем scan()
singleClaculation.start(slt)
//а вот тут тяжелая задача закончилась, далее scan() найдет новую работу
multiCalculation.start(slt)

Ведь в scala «The ExecutionContextTaskSupport uses the default execution context implementation found in scala.concurrent, and it reuses the thread pool used in scala.concurrent»

Убираем singleCalculation, доля scan в выхлопе падает до 1,7% и больше всего, как и должен, жрет run()

RedPossum ★★★★★ ()
Последнее исправление: RedPossum (всего исправлений: 1)
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.