Почему этот код Java не использует все ядра процессора? - PullRequest
14 голосов
/ 19 мая 2010

Прилагаемый простой код Java должен загружать все доступное ядро ​​процессора при запуске с правильными параметрами. Так, например, вы начинаете это с

Java VMTest 8 int 0

и он запустит 8 потоков, которые не делают ничего, кроме цикла и добавления 2 к целому числу. То, что работает в регистрах и даже не выделяет новую память.

Проблема, с которой мы сталкиваемся сейчас, заключается в том, что мы не загружаем 24-ядерную машину (2 сокета AMD с 12 ядрами в каждой) при запуске этой простой программы (конечно, с 24 потоками). Подобные вещи случаются с 2 программами каждые 12 потоков или меньших машин.

Итак, мы подозреваем, что JVM (Sun JDK 6u20 в Linux x64) плохо масштабируется.

Кто-нибудь видел подобные вещи или имеет возможность запустить его и сообщить, хорошо ли он работает на его / ее машине (> = только 8 ядер, пожалуйста)? Идеи?

Я пробовал это на Amazon EC2 с 8 ядрами, но виртуальная машина, кажется, работает не так, как реальная коробка, поэтому загрузка ведёт себя совершенно странно.

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 ]

10 голосов
/ 19 мая 2010

Я не вижу ничего плохого в вашем коде.

Однако, к сожалению, вы не можете указать сходство процессора в Java. Таким образом, это фактически остается за ОС, а не JVM. Это все о том, как ваша ОС обрабатывает потоки.

Вы можете разделить свои потоки Java на отдельные процессы и обернуть их в собственный код, чтобы поместить один процесс на ядро. Это, конечно, усложняет общение, поскольку оно будет межпроцессным, а не межпоточным. Как бы то ни было, именно так работают такие приложения для грид-вычислений, как boink.

В противном случае вы зависите от ОС для планирования потоков.

4 голосов
/ 19 мая 2010

Я думаю, это присуще JVM / OS и не обязательно вашему коду. Проверьте различные документы по настройке производительности JVM от Sun, например, http://ch.sun.com/sunnews/events/2009/apr/adworkshop/pdf/5-1-Java-Performance.pdf, который предлагает использовать numactl в Linux для установки сродства.

Удачи!

2 голосов
/ 22 апреля 2013

Очевидно, что ваша виртуальная машина работает в так называемом «клиентском» режиме, где все потоки Java отображаются в один собственный поток ОС и, следовательно, выполняются одним ядром процессора. Попробуйте вызвать JVM с ключом -server, это должно исправить проблему.

Если вы нашли: Error: no 'server' JVM найдено, вам придется скопировать каталог server из каталога JDK jre\bin в JRE bin.

1 голос
/ 20 октября 2010

uname -a 2.6.18-194.11.4.el5 # 1 SMP вт 21 сентября 05:04:09 EDT 2010 x86_64 x86_64 x86_64 GNU / Linux

Intel (R) Xeon (R) CPU E5530@ 2,40 ГГц http://browse.geekbench.ca/geekbench2/view/182101

Java 1.6.0_20-b02

16 ядер, программа потребляла 100% ресурсов процессора, как показывает vmstat

Интересно, что я пришел к этой статье, потому что яЯ подозреваю, что мое приложение не использует все ядра, поскольку загрузка процессора никогда не увеличивается, но время отклика начинает ухудшаться

0 голосов
/ 19 мая 2010

Я заметил даже на C, что у узкой петли часто бывают подобные проблемы. Вы также увидите довольно большие различия в зависимости от ОС.

В зависимости от используемого вами инструмента отчетности он может не сообщать о процессоре, используемом некоторыми основными службами.

Java имеет тенденцию быть довольно дружелюбной. Вы можете попробовать то же самое в Linux, но установите приоритет процесса на какое-то отрицательное число и посмотрите, как он работает.

Установка приоритетов потоков внутри приложения также может немного помочь, если ваш jvm не использует зеленые потоки.

много переменных.

...