Scala的循环反应的性能非常差。为什么?

时间:2011-08-26 13:06:18

标签: java performance scala

我只是在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 个答案:

答案 0 :(得分:6)

您正在测试两个非常不同的代码。我们考虑使用Java,例如:

    while (true) {

其他“演员”有机会接管线程并自行处理吗?这个“演员”几乎占据了主线。如果你创建了100000个,那么你会看到JVM在竞争“演员”的压力下被粉碎,或者看到一些人得到所有的处理时间,而其他人则在萎缩。

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

为什么要将事件从队列中取出而不检查是否可以处理?如果无法处理,您将失去该事件。如果您将其添加回队列,它将在同一来源发送的其他事件之后结束。

这些只是Scala代码执行的两件事,而Java代码则没有。

答案 1 :(得分:4)

总的来说,这是一项非常不科学的测试。没有热身。迭代次数少。非常非常不稳定。看看google caliper或者其他有关制作更好的微基准测试的想法。

一旦你的数字清楚了:将它编译成scala,然后将其反编译成java。答案可能会跳出来。

我认为在你的情况下,它可能是演员的配置。尝试akka。

答案 2 :(得分:3)

我有一台带4个处理器的机器。如果我运行你的java代码,我会在一个处理器上获得完整的处理器使用率(25%)。也就是说,您正在使用单个线程。

如果我运行你的scala代码,我会完全使用所有处理器,我有四个线程。

因此,我怀疑发生了两件事情:您正在获得争用更新计数,和/或计数未正确递增。

此外,您在循环中进行的测试是Scala中的模式匹配,但在Java中是一个简单的相等,但我怀疑这是次要部分。

答案 3 :(得分:1)

Actors用于导致有意义计算的小消息,而不是如上所述的逐元素数据处理。

您的Actor代码与具有多个线程的ExecutorService实际上更具可比性,其中每条消息代表一个新的Runnable / Callable,而不是您在Java代码中的内容。

您的基准测试实际上是在比较“工作线程可以消耗队列中的项目的速度”与“scala将消息发送到邮箱的速度有多快,通知并安排演员以及处理消息”。这不是一回事,也不适合同样的目的。

无论如何,Scala也可以使用Java线程。 Scala只是为您提供了一个额外的(更安全,更简单,基于通信的)并发机制。

答案 4 :(得分:0)

为了控制流量,

循环并做出反应抛出异常。这意味着给线程池提供了两个任务,其中只有一个实际工作。即使JVM成功地将它们优化为longjmps,异常也比常规返回要昂贵得多。