Производительность реакции Scala на петлю такая низкая. Зачем? - PullRequest
4 голосов
/ 26 августа 2011

Я просто пишу одну демонстрацию производителя-потребителя в Scala и Java.Демонстрация показывает, что производительность Scala очень низкая.Мой код неверен?

Java AVG: 1933534.1171935236
Scala AVG: 103943.7312328648

Код Scala:

import scala.actors.Actor.actor
import scala.actors.Actor.loop
import scala.actors.Actor.react
import scala.concurrent.ops.spawn
object EventScala {

case class Event(index: Int)

def test() {
    val consumer = actor {
        var count = 0l
        val start = System.currentTimeMillis()
        loop {
            react {
                case Event(c) => count += 1
                case "End" =>
                    val end = System.currentTimeMillis()
                    println("Scala AVG:" + count * 1000.0 / (end - start))
                    exit()
            }
        }
    }
    var running = true;
    for (i <- 0 to 1) {
        {
            spawn {
                while (running) {
                    consumer ! Event(0)
                }
                consumer!"End"
            }
        }
    }
    Thread.sleep(5000)
    running = false
}

def main(args: Array[String]): Unit = {
    test
}

}

Код Java:

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;

public class EventJava {
static BlockingQueue<Event> queue = new LinkedBlockingQueue<EventJava.Event>();
static volatile boolean running = true;
static volatile Event sentinel = new Event(0);

static class Event {
    final int index;

    public Event(int index) {
        this.index = index;
    }
}

static class Consumer implements Runnable {
    @Override
    public void run() {
        long count = 0;
        long start = System.currentTimeMillis();
        while (true) {
            try {
                Event event = queue.take();
                if (event == sentinel) {
                    long end = System.currentTimeMillis();
                    System.out.println("Java AVG:" + count * 1000.0
                            / (end - start));
                    break;
                }
                count++;
            } catch (InterruptedException e) {
            }
        }
    }
}

static class Producer implements Runnable {
    @Override
    public void run() {
        while (running) {
            queue.add(new Event(1));
        }
        queue.add(sentinel);
    }
}

static void test() throws InterruptedException {
    ExecutorService pool = Executors.newCachedThreadPool();
    pool.submit(new Consumer());
    pool.execute(new Producer());
    pool.execute(new Producer());
    Thread.sleep(5000);
    running = false;
    pool.shutdown();
}

public static void main(String[] args) throws InterruptedException {
    test();
}

}

Ответы [ 5 ]

6 голосов
/ 26 августа 2011

Вы тестируете два совершенно разных кода. Давайте рассмотрим Java, например:

    while (true) {

Где есть возможность для других "актеров" взять на себя управление потоком и выполнить свою собственную обработку? Этот "актер" в значительной степени затягивает нить. Если вы создадите 100000 из них, вы увидите, как JVM сокрушится под тяжестью конкурирующих «актеров», или увидите, как некоторые получают все время обработки, в то время как другие томятся.

            Event event = queue.take();
            if (event == sentinel) {

Почему вы вынимаете событие из очереди, не проверяя, может ли оно быть обработано или нет? Если это не может быть обработано, вы потеряете событие. Если вы добавили его обратно в очередь, он закончится после других событий, отправленных тем же источником.

Это всего лишь две вещи, которые делает код Scala, а Java - нет.

4 голосов
/ 26 августа 2011

В целом, это очень ненаучный тест.Нет прогрева.Небольшое количество итераций.Очень очень не научен.Посмотрите на Google Caliper или что-то подобное, чтобы узнать, как сделать более эффективные микро-тесты.

Как только ваши цифры станут ясными: скомпилируйте их в scala, а затем декомпилируйте в java.Ответ может выскочить.

Я думаю, в вашем случае это может быть конфигурация актеров.Попробуйте также akka.

3 голосов
/ 26 августа 2011

У меня есть машина с 4 процессорами. Если я запускаю ваш код Java, я получаю полное использование процессора на одном процессоре (25%). То есть вы используете один поток.

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

Так что я подозреваю, что происходят две вещи: вы получаете счетчик обновлений конкуренции и / или счет не увеличивается правильно.

Кроме того, тест, который вы выполняете в цикле, - это сопоставление с образцом в Scala, но это простое равенство в Java, но я подозреваю, что это второстепенная часть.

1 голос
/ 01 июля 2012

Актеры предназначены для небольших сообщений, которые приводят к осмысленным вычислениям, а не для поэлементной обработки данных, как указано выше.

Ваш код Actor действительно более сопоставим с ExecutorService с несколькими потоками, где каждое сообщение представляет новый выполняемый / вызываемый элемент, а не то, что у вас есть в коде Java.

Ваш тестдействительно сравнивает «как быстро рабочий поток может потреблять элемент из очереди» с «насколько быстро скала может отправить сообщение в почтовый ящик, уведомить и запланировать актера и обработать сообщение».Это просто не то же самое, и это не подходит для той же цели.

Несмотря на это, Scala может использовать потоки Java тоже.Scala просто предоставляет вам дополнительный (более безопасный, простой и основанный на коммуникации) механизм параллелизма.

0 голосов
/ 27 августа 2011
Цикл

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...