首页 > 解决方案 > Groovy ASTBuilder 多线程性能不佳

问题描述

我在一个项目中使用 Groovy 的 ASTBuilder(版本 2.5.5)。它用于解析和分析通过 REST API 接收到的 groovy 表达式。此 REST 服务接收数千个请求,并即时完成分析。

我注意到多线程环境中存在一些严重的性能问题。下面是一个模拟,并行运行 100 个线程:

int numthreads = 100;

final Callable<Void> task = () -> {
    long initial = System.currentTimeInMillis();
    // Simple rule
    new AstBuilder().buildFromString("a+b");

    System.out.print(String.format("\n\nThread took %s ms.", 
        System.currentTimeInMillis() - initial));
    return null;
};

final ExecutorService executorService = Executors.newFixedThreadPool(numthreads);
final List<Callable<Void>> tasks = new ArrayList<>();
while (numthreads-- > 0) {
    tasks.add(task);
}
for (Future<Void> future : executorService.invokeAll(tasks)) {
    future.get();
}

我尝试使用不同的线程负载。数字越大,速度越慢。

但是,如果我对线程进行序列化(例如将池大小设置为 1),我会得到更好的结果,每个线程大约 10 毫秒。有人可以帮我理解为什么会这样吗?

标签: javamultithreadinggroovygroovyshell

解决方案


执行多线程代码,计算机在物理 CPU 内核之间共享线程。这意味着线程数超过内核数越多,您从每个线程中获得的收益就越少。在您的示例中,线程数随着任务数的增加而增加。因此,随着任务数量的增加,每个 CPU 内核都被迫处理越来越多的线程。同时,您可能会注意到numthreads = 1和之间的差异numthreads = 4非常小。因为在这种情况下,每个核心只处理少数(甚至只是一个)线程。不要将线程数设置为多于物理 CPU 线程数,因为这没有多大意义。

此外,在您的示例中,您试图比较不同数量的线程如何执行不同数量的任务。但是为了查看多线程代码的效率,您必须比较不同数量的线程在相同数量的任务下的执行情况。我将通过以下方式更改示例:

int threadNumber = 16;
int taskNumber = 200;

//...task method

final ExecutorService executorService = Executors.newFixedThreadPool(threadNumber);
final List<Callable<Void>> tasks = new ArrayList<>();
while (taskNumber-- > 0) {
    tasks.add(task);
}

long start = System.currentTimeMillis();
for (Future<Void> future : executorService.invokeAll(tasks)) {
    future.get();
}
long end = System.currentTimeMillis() - start;
System.out.println(end);
executorService.shutdown();

试试这个代码threadNumber=1,让我们说,threadNumber=16你会看到不同之处。


推荐阅读