Почему этот код Java не использует все ядра ЦП?
Приложенный простой Java-код должен загружать все доступные ядра процессора при запуске с правильными параметрами. Например, вы начинаете с
java VMTest 8 int 0
и он запустит 8 потоков, которые сделают не что иное, как цикл и добавят 2 к целому. Что-то, что работает в регистрах и даже не выделяет новую память.
Проблема, с которой мы сталкиваемся сейчас, заключается в том, что мы не получаем загрузочную 24-ядерную машину (разъемы AMD 2 с 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);
}
}
Ответы
Ответ 1
Я не вижу ничего плохого в вашем коде.
Однако, к сожалению, вы не можете указать сродство процессора в Java. Таким образом, это фактически оставлено до ОС, а не JVM. Все о том, как ваша ОС обрабатывает потоки.
Вы можете разделить потоки Java на отдельные процессы и обернуть их в собственный код, чтобы поместить один процесс на ядро. Это, конечно, усложняет общение, поскольку это будет межпроцессный, а не межпоточный. Во всяком случае, так популярны сетевые вычислительные приложения, такие как работа в режиме реального времени.
В противном случае вы можете починить ОС для планирования потоков.
Ответ 2
Я бы предположил, что это присуще JVM/OS, а не обязательно вашему коду. Проверьте различные документы настройки производительности JVM от Sun, например. http://ch.sun.com/sunnews/events/2009/apr/adworkshop/pdf/5-1-Java-Performance.pdf, который предлагает использовать numactl
в Linux для установки сродства.
Удачи!
Ответ 3
Очевидно, ваша виртуальная машина работает в так называемом "клиентском" режиме, где все потоки Java сопоставляются с одним потоком ОС и, следовательно, управляются одним ядром ЦП. Попробуйте вызвать JVM с помощью переключателя -server
, это должно устранить проблему.
Если вы получите найденный Error: no 'server' JVM
, вам придется скопировать каталог server
из каталога JDK jre\bin
в JRE bin
.
Ответ 4
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
16cores, программа потребляла 100% процессор, как показано vmstat
Интересно, что я пришел к этой статье, потому что я подозреваю, что мое приложение не использует все ядра, поскольку использование процессора никогда не увеличивается, но время отклика начинает ухудшаться
Ответ 5
Я заметил даже на C, что узкая петля часто имеет такие проблемы. Вы также увидите довольно значительные различия в зависимости от ОС.
В зависимости от используемого средства отчетности он может не сообщать CPU, используемому некоторыми основными службами.
Java, как правило, очень дружелюбный. Вы можете попробовать одно и то же в linux, но установите приоритет процесса на некоторое отрицательное число и посмотрите, как он действует.
Настройка приоритетов потоков внутри приложения может помочь немного, если ваш jvm не использует зеленые потоки.
Множество переменных.