为什么这段 Java 代码没有利用所有 CPU 核心?

发布于 2024-09-02 00:39:13 字数 2798 浏览 5 评论 0 原文

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

java VMTest 8 int 0

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

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

因此我们怀疑 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);
    }
}

The attached simple Java code should load all available cpu core when starting it with the right parameters. So for instance, you start it with

java VMTest 8 int 0

and it will start 8 threads that do nothing else than looping and adding 2 to an integer. Something that runs in registers and not even allocates new memory.

The problem we are facing now is, that we do not get a 24 core machine loaded (AMD 2 sockets with 12 cores each), when running this simple program (with 24 threads of course). Similar things happen with 2 programs each 12 threads or smaller machines.

So our suspicion is that the JVM (Sun JDK 6u20 on Linux x64) does not scale well.

Did anyone see similar things or has the ability to run it and report whether or not it runs well on his/her machine (>= 8 cores only please)? Ideas?

I tried that on Amazon EC2 with 8 cores too, but the virtual machine seems to run different from a real box, so the loading behaves totally strange.

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);
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

为人所爱 2024-09-09 00:39:13

我没有看到你的代码有什么问题。

然而,遗憾的是,您无法在 Java 中指定处理器关联性。因此,这实际上是由操作系统决定的,而不是 JVM。这完全取决于操作系统如何处理线程。

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

否则,您将受到操作系统的支配来安排线程。

I don't see anything wrong with your code.

However, unfortunately, you can't specify the processor affinity in Java. So, this is actually left up to the OS, not the JVM. It's all about how your OS handles threads.

You could split your Java threads into separate processes and wrap them up in native code, to put one process per core. This does, of course, complicate communication, as it will be inter-process rather than inter-thread. Anyway, this is how popular grid computing applications like boink work.

Otherwise, you're at the mercy of the OS to schedule the threads.

娇纵 2024-09-09 00:39:13

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

祝你好运!

I would guess this is inherent to the JVM/OS and not necessarily your code. Check the various JVM performance tuning docs from Sun, e.g. http://ch.sun.com/sunnews/events/2009/apr/adworkshop/pdf/5-1-Java-Performance.pdf which suggests using numactl on Linux to set the affinity.

Good luck!

泪痕残 2024-09-09 00:39:13

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

如果您收到:错误:找不到“服务器”JVM,则必须从 JDK 的 jre\bin 复制 server 目录JRE 的 bin 目录。

Apparently your VM is running in so-called "client" mode, where all Java threads are mapped to one native OS thread and consequently are run by one single CPU core. Try to invoke the JVM with -server switch, this should correct the problem.

If you get an: Error: no 'server' JVM found, you'll have to copy the server directory from a JDK's jre\bin directory to JRE's bin.

朮生 2024-09-09 00:39:13

uname -a
2.6.18-194.11.4.el5 #1 SMP 9 月 21 日星期二 05: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 利用率从未增加,但响应时间开始恶化

uname -a
2.6.18-194.11.4.el5 #1 SMP Tue Sep 21 05: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, the program consumed 100% cpu as shown by vmstat

Interestingly I came to this article because I am suspecting my application is not utilizing all the cores as the cpu utilisation never increases but the response time starts deteriorating

So要识趣 2024-09-09 00:39:13

我注意到即使在 C 语言中,紧循环也经常会出现这样的问题。根据操作系统的不同,您还会看到相当大的差异。

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

Java 往往非常友好。您可以在 Linux 中尝试相同的操作,但将进程优先级设置为某个负数,然后看看它的行为如何。

如果您的 jvm 不使用绿色线程,那么在应用程序内设置线程优先级也可能会有所帮助。

很多变数。

I've noticed even on C that a tight loop often has issues like that. You'll also see pretty vast differences depending on OS.

Depending on the reporting tool you are using, it may not report the CPU used by some core services.

Java tends to be pretty friendly. You might try the same thing in linux but set the process priority to some negative number and see how it acts.

Setting thread priorities inside the app may help a little too if your jvm isn't using green threads.

Lots of variables.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文