100%CPU使用率的Java线程优化

时间:2012-04-12 02:09:16

标签: java multithreading performance

我有一个应用程序接受队列上的工作,然后旋转,以便在独立线程上完成。线程数量不大,比如高达100,但这些都是密集型任务,可以快速将CPU提升到100%。

为了最快地完成大部分工作:当我需要做更多工作并让Java线程调度程序处理分配工作时,我最好只启动更多线程,或者更聪明地管理工作负载以保持工作量低于100%的CPU让我更快?

该机器专用于我的java应用程序。

修改

感谢您的精彩投入!

任务具有不同的复杂性并且涉及I / O,因此具有4个低线程池可能仅将CPU运行到20%。我无法知道有多少任务实际上将CPU带到100%。

我的想法是我应该通过RMI监控CPU并动态拨打上下工作,或者我应该不关心并让操作系统处理它。

5 个答案:

答案 0 :(得分:13)

如果在并行线程中有太多同时执行计算密集型任务,则会很快达到收益递减的程度。实际上,如果有N个处理器(核心),那么你不需要超过N个这样的线程。现在,如果任务偶尔暂停I / O或用户交互,那么正确的数字可能会稍大一些。但一般来说,如果在任何一个时刻有更多的线程想要进行计算而不是有可用的核心,那么你的程序就会浪费时间在上下文切换上 - 也就是说,调度会让你付出代价。

答案 1 :(得分:6)

  

为了最快地完成大部分工作:当我需要做更多工作并让Java线程调度程序处理分配工作时,我最好只启动更多线程,或者更聪明地管理工作负载以保持工作量CPU低于100%让我更快?

随着您添加越来越多的线程,上下文切换,内存缓存刷新,内存缓存溢出以及内核和JVM线程管理所产生的开销也会增加。当您的线程占用CPU时,它们的内核优先级降至最低,并且它们将达到时间片最小值。随着越来越多的线程占用内存,它们会溢出各种内部CPU内存缓存。 CPU需要从较慢的内存中交换作业的可能性更高。 JVM内部存在更多互斥锁本地争用,可能还有一些(可能很小的)增量每线程和对象带宽GC开销。根据用户任务的同步程度,更多线程会导致内存刷新和锁争用增加。

对于任何程序和任何体系结构,线程可以最佳地利用可用的处理器和IO资源,同时限制内核和JVM开销。反复寻找最佳位置将需要多次迭代和一些猜测。

我建议使用Executors.newFixedThreadPool(SOME_NUMBER);并将作业提交给它。然后,您可以执行多次运行,根据工作和框架的体系结构,上下调整线程数,直到找到同时运行的最佳池数。

然而,要了解,最佳线程数将根据处理器数量和其他可能非常重要的因素而有所不同。如果它们阻塞磁盘或网络IO资源,则可能需要更多线程。如果他们正在做的工作主要是基于CPU的,那么线程就会减少。

答案 2 :(得分:6)

您的CPU以100%的速度运行这一事实并不能说明他们正在做多忙有用工作。在您的情况下,您使用的线程数多于核心数,因此100%包含一些上下文切换并且不必要地使用内存(对100个线程影响很小),这是次优的。

对于CPU密集型任务,我通常使用这个习语:

private final int NUM_THREADS = Runtime.getRuntime().availableProcessors() + 1;
private final ExecutorService executor = Executors.newFixedThreadPool(NUM_THREADS);

正如其他人所指出的那样,使用更多线程只会引入不必要的上下文切换。

显然,如果任务执行了一些I / O和其他阻塞操作,这是不适用的,更大的池是有意义的。

修改

要回复@MartinJames评论,我已经运行了一个(简单化)基准测试 - 结果显示,从池大小=处理器数量+ 1到100只会略微降低性能(让我们称之为5%) - 去较高的数字(1000和10000)确实显着地影响了性能。

  

结果是10次运行的平均值:
  泳池大小:9:238毫秒。 //(NUM_CORES + 1)
  泳池大小:100:245毫秒   泳池大小:1000:319毫秒   池大小:10000:2482毫秒。

代码:

public class Test {

    private final static int NUM_CORES = Runtime.getRuntime().availableProcessors();
    private static long count;
    private static Runnable r = new Runnable() {

        @Override
        public void run() {
            int count = 0;
            for (int i = 0; i < 100_000; i++) {
                count += i;
            }
            Test.count += count;
        }
    };

    public static void main(String[] args) throws Exception {
        //warmup
        runWith(10);

        //test
        runWith(NUM_CORES + 1);
        runWith(100);
        runWith(1000);
        runWith(10000);
    }

    private static void runWith(int poolSize) throws InterruptedException {
        long average = 0;
        for (int run = 0; run < 10; run++) { //run 10 times and take the average
            Test.count = 0;
            ExecutorService executor = Executors.newFixedThreadPool(poolSize);
            long start = System.nanoTime();
            for (int i = 0; i < 50000; i++) {
                executor.submit(r);
            }
            executor.shutdown();
            executor.awaitTermination(10, TimeUnit.SECONDS);
            long end = System.nanoTime();
            average += ((end - start) / 1000000);
            System.gc();
        }
        System.out.println("Pool size: " + poolSize + ": " + average / 10 + " ms.  ");
    }
}

答案 3 :(得分:1)

'会变得更聪明并且管理工作量以保持CPU低于100%会让我更快吗?'

可能不是。

正如其他人发布的那样,如果大多数任务都是CPU密集型的话,那么线程池的100个线程太多了。它对典型系统的性能没有太大影响 - 如果过多则会导致4个线程坏,而400个则坏。

你是如何决定100个线程的?为什么不16,说?

'线程数量不大,比如高达100' - 它有变化吗?只需在启动时创建16并停止管理它们 - 只需将队列传递给它们并忘记它们。

可怕的想法 - 你没有为每项任务创建一个新线程,是吗?

答案 4 :(得分:0)

您应该保持100%的使用率,但使用最少的线程数。 100个线程看起来太多了。