为什么这个Java代码不使用所有CPU内核?

时间:2010-05-19 16:03:28

标签: java scalability cpu multicore scaling

附加的简单Java代码应该在使用正确的参数启动时加载所有可用的cpu核心。例如,您可以使用

启动它
  

java VMTest 8 int 0

它将启动8个线程,除了循环并将2添加到整数之外什么都不做。在寄存器中运行的东西,甚至不分配新的内存。

我们现在面临的问题是,在运行这个简单的程序(当然有24个线程)时,我们没有加载24核机器(AMD 2个插槽,每个12个核心)。类似的事情发生在每12个线程或更小的机器的2个程序。

所以我们怀疑JVM(Linux x64上的Sun JDK 6u20)不能很好地扩展。

有没有人看到类似的东西或有能力运行它并报告它是否在他/她的机器上运行良好(> =仅8个核心)?想法?

我在带有8个内核的Amazon EC2上尝试过,但是虚拟机似乎与真正的盒子不同,因此加载的行为非常奇怪。

package com.test;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

public class VMTest
{
    public class IntTask implements Runnable 
    {
        @Override
        public void run()
        {
            int i = 0;

            while (true)
            {
                i = i + 2;
            }
        }
    }
    public class StringTask implements Runnable 
    {
        @Override
        public void run()
        {
            int i = 0;

            String s;
            while (true)
            {
                i++;
                s = "s" + Integer.valueOf(i);
            }
        }
    }
    public class ArrayTask implements Runnable 
    {
        private final int size; 
        public ArrayTask(int size)
        {
            this.size = size;
        }
        @Override
        public void run()
        {
            int i = 0;

            String[] s;
            while (true)
            {
                i++;
                s = new String[size];
            }
        }
    }

    public void doIt(String[] args) throws InterruptedException
    {
        final String command = args[1].trim();

        ExecutorService executor = Executors.newFixedThreadPool(Integer.valueOf(args[0]));
        for (int i = 0; i < Integer.valueOf(args[0]); i++)
        {
            Runnable runnable = null;
            if (command.equalsIgnoreCase("int"))
            {
                runnable = new IntTask();
            }
            else if (command.equalsIgnoreCase("string"))
            {
                runnable = new StringTask();
            }
            Future<?> submit = executor.submit(runnable);
        }
        executor.awaitTermination(1, TimeUnit.HOURS);
    }

    public static void main(String[] args) throws InterruptedException
    {
        if (args.length < 3)
        {
            System.err.println("Usage: VMTest threadCount taskDef size");
            System.err.println("threadCount: Number 1..n");
            System.err.println("taskDef: int string array");
            System.err.println("size: size of memory allocation for array, ");
            System.exit(-1);
        }

        new VMTest().doIt(args);
    }
}

5 个答案:

答案 0 :(得分:10)

我认为您的代码没有任何问题。

但是,遗憾的是,您无法在Java中指定处理器关联。所以,这实际上是由操作系统而不是JVM完成的。这都是关于你的操作系统如何处理线程的。

您可以将Java线程拆分为单独的进程并将其包装在本机代码中,以便为每个核心放置一个进程。当然,这确实使通信变得复杂,因为它将是进程间而不是线程间。无论如何,这就是像boink这样流行的网格计算应用程序。

否则,您将受操作系统的限制来安排线程。

答案 1 :(得分:4)

我猜这是JVM / OS固有的,不一定是你的代码。检查Sun的各种JVM性能调优文档,例如: http://ch.sun.com/sunnews/events/2009/apr/adworkshop/pdf/5-1-Java-Performance.pdf建议在Linux上使用numactl来设置亲和力。

祝你好运!

答案 2 :(得分:2)

显然,您的VM正在所谓的“客户端”模式下运行,其中所有Java线程都映射到一个本机OS线程,因此由一个CPU核心运行。尝试使用-server开关调用JVM,这应该可以解决问题。

如果找到Error: no 'server' JVM,则必须将server目录从JDK的jre\bin目录复制到JRE的bin

答案 3 :(得分:1)

uname -a 2.6.18-194.11.4.el5#1 SMP Tue Sep 21 21:04:09 EDT 2010 x86_64 x86_64 x86_64 GNU / Linux

Intel(R)Xeon(R)CPU E5530 @ 2.40GHz http://browse.geekbench.ca/geekbench2/view/182101

Java 1.6.0_20-b02

16cores,该程序消耗了100%的cpu,如vmstat所示

有趣的是我来到这篇文章是因为我怀疑我的应用程序没有使用所有内核,因为cpu利用率从未增加但响应时间开始恶化

答案 4 :(得分:0)

我甚至在C上注意到紧密循环经常会出现这样的问题。根据操作系统的不同,您还会看到相当大的差异。

根据您使用的报告工具,它可能不会报告某些核心服务使用的CPU。

Java往往非常友好。您可以在linux中尝试相同的操作,但将进程优先级设置为某个负数,并查看它的行为方式。

如果你的jvm没有使用绿色线程,在app中设置线程优先级可能会有所帮助。

很多变数。