Это использует Scala 2.8 Актеры.У меня долгая работа, которую можно распараллелить.Он состоит из около 650 000 единиц работы.Я делю его на 2600 различных отдельных подзадач, и для каждой из них я создаю нового актера:
actor {
val range = (0L to total by limit)
val latch = new CountDownLatch(range.length)
range.foreach { offset =>
actor {
doExpensiveStuff(offset,limit)
latch.countDown
}
}
latch.await
}
Это работает довольно хорошо, но в целом для выполнения требуется 2 + ч.Проблема заключается в том, что в то же время любые другие действующие лица, которых я создаю для выполнения обычных задач, кажутся голодными первоначальными 2600 действующими лицами, которые также терпеливо ожидают своего времени, чтобы быть запущенным в потоке, но ждут дольше, чем любые новые действующие лица, которыедавай.
Как мне избежать этого голода?
Начальные мысли:
- Вместо 2600 актеров, используйте одного актера, который последовательно пропускает через большойкуча работы.Мне это не нравится, потому что я бы хотел, чтобы эта работа была закончена раньше, разделив ее на части.
- Вместо 2600 актеров используйте двух актеров, каждый из которых обрабатывает свою половину всего рабочего набора.Это может работать лучше, но что, если моя машина имеет 8 ядер?Я, вероятно, хотел бы использовать больше, чем это.
ОБНОВЛЕНИЕ
Некоторые люди вообще ставили под сомнение использование Актеров,тем более, что возможность передачи сообщений не использовалась внутри рабочих.Я предполагал, что Actor - это очень легкая абстракция вокруг ThreadPool на том же уровне производительности или около того же уровня, что и простое кодирование выполнения на основе ThreadPool вручную.Поэтому я написал небольшой тест:
import testing._
import java.util.concurrent._
import actors.Futures._
val count = 100000
val poolSize = 4
val numRuns = 100
val ActorTest = new Benchmark {
def run = {
(1 to count).map(i => future {
i * i
}).foreach(_())
}
}
val ThreadPoolTest = new Benchmark {
def run = {
val queue = new LinkedBlockingQueue[Runnable]
val pool = new ThreadPoolExecutor(
poolSize, poolSize, 1, TimeUnit.SECONDS, queue)
val latch = new CountDownLatch(count)
(1 to count).map(i => pool.execute(new Runnable {
override def run = {
i * i
latch.countDown
}
}))
latch.await
}
}
List(ActorTest,ThreadPoolTest).map { b =>
b.runBenchmark(numRuns).sum.toDouble / numRuns
}
// List[Double] = List(545.45, 44.35)
Я использовал абстракцию Future в ActorTest, чтобы избежать передачи сообщения другому актеру, чтобы показать, что работа выполнена.Я был удивлен, обнаружив, что мой код актера был в 10 раз медленнее.Обратите внимание, что я также создал свой ThreadPoolExecutor с начальным размером пула, с которым создается пул Actor по умолчанию.
Оглядываясь назад, мне кажется, что я, возможно, злоупотребил абстракцией Actor.Я собираюсь изучить использование отдельных ThreadPools для этих отдельных, дорогостоящих и длительных задач.