我只是在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();
}
}
答案 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,异常也比常规返回要昂贵得多。