为什么此 Java 代码未使用所有 CPU 内核?

2022-09-03 14:30:41

附加的简单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);
    }
}

答案 1

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

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

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

否则,您将任由操作系统来调度线程。


答案 2

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

祝你好运!


推荐