Принуждение нескольких потоков использовать несколько процессоров, когда они доступны
Я пишу программу Java, которая использует много процессора из-за природы того, что он делает. Однако многие из них могут работать параллельно, и я сделал свою программу многопоточной. Когда я запускаю его, кажется, что он использует только один процессор, пока ему не понадобится больше, чем он использует другой процессор - есть ли что-нибудь, что я могу сделать в Java, чтобы заставить разные потоки работать на разных ядрах/процессорах?
10 ответов:
когда я запускаю его, он только кажется использовать один процессор, пока ему не понадобится больше, чем он использует другой процессор - есть ли что-нибудь I может сделать в Java, чтобы заставить разные потоки для запуска на разных ядра / процессоры?
я интерпретирую эту часть вашего вопроса как означающую, что вы уже обратились к проблеме создания многопоточности вашего приложения. И несмотря на это, он не сразу начинает использовать несколько ядер.
ответ на вопрос " есть ли любой способ заставить ..."это (АФАИК) не напрямую. Ваша JVM и / или хост-ОС решают, сколько "собственных" потоков использовать и как эти потоки сопоставляются с физическими процессорами. У вас есть несколько вариантов настройки. Например, я нашел на этой странице который говорит о том, как настроить Java threading на Solaris. И на этой странице говорит о других вещах, которые могут замедлить многопоточное приложение.
есть два основных способа многопоточности в Java. Каждая логическая задача, созданная с помощью этих методов, должна выполняться на новом ядре, когда это необходимо и доступно.
способ один: определите объект Runnable или Thread (который может принимать Runnable в конструкторе) и запустите его с потоком.метод Start. Он будет выполняться на любом ядре, которое дает ему ОС-как правило, менее загруженный.
руководство: определение и запуск Threads
Способ второй: определите объекты, реализующие интерфейс Runnable (если они не возвращают значения) или Callable (если они это делают), которые содержат ваш код обработки. Передайте их как задачи в ExecutorService из java.утиль.concurrent пакет. Ява.утиль.параллельный.Класс Executors имеет множество методов для создания стандартных, полезных видов ExecutorServices. ссылке для исполнителей учебник.
из личного опыта, Исполнители фиксированные и кэшированные пулы потоков очень хороши,хотя вы захотите настроить количество потоков. Во время выполнения.getRuntime().доступные процессоры () могут использоваться во время выполнения для подсчета доступных ядер. Вам нужно будет закрыть пулы потоков, когда ваше приложение будет готово, иначе приложение не выйдет, потому что потоки ThreadPool остаются запущенными.
получение хорошей производительности многоядерных иногда сложно, и полный gotchas:
- дисковый ввод-вывод замедляется вниз много, когда бегут параллельный. Только один поток должен выполнять чтение/запись на диск одновременно.
- синхронизация объектов обеспечивает безопасность многопоточных операций, но замедляет работу.
- если задачи слишком тривиальный (малые биты работы, исполняют быстро) накладные расходы на управление ими в ExecutorService стоит больше, чем вы получаете от нескольких ядер.
- создание новых объектов потока медленно. В ExecutorServices будет пытаться повторно использовать существующие потоки, если вероятный.
- всякие сумасшедшие вещи могут произойти, когда несколько потоков работают над чем-то. Держите свою систему простой и постарайтесь сделать задачи логически отличными и не взаимодействующими.
еще одна проблема: контроль работы трудно! Рекомендуется иметь один поток диспетчера, который создает и отправляет задачи, а затем несколько рабочих потоков с рабочими очередями (используя ExecutorService).
Я просто касаюсь ключевых моментов здесь -- многопоточный программирование считается одним из самых сложных предметов программирования многими экспертами. Это неинтуитивно, сложно, и абстракции часто слабы.
Edit -- пример использования ExecutorService:
public class TaskThreader { class DoStuff implements Callable { Object in; public Object call(){ in = doStep1(in); in = doStep2(in); in = doStep3(in); return in; } public DoStuff(Object input){ in = input; } } public abstract Object doStep1(Object input); public abstract Object doStep2(Object input); public abstract Object doStep3(Object input); public static void main(String[] args) throws Exception { ExecutorService exec = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); ArrayList<Callable> tasks = new ArrayList<Callable>(); for(Object input : inputs){ tasks.add(new DoStuff(input)); } List<Future> results = exec.invokeAll(tasks); exec.shutdown(); for(Future f : results) { write(f.get()); } } }
во-первых, вы должны доказать себе, что ваша программа будет работать быстрее на нескольких ядрах. Многие операционные системы прикладывают усилия для запуска программных потоков на одном ядре по возможности.
работает на одном ядре имеет много преимуществ. Кэш ЦП горячий, что означает, что данные для этой программы загружаются в ЦП. Объекты блокировки / монитора / синхронизации находятся в кэше ЦП, что означает, что другим ЦП не нужно делать кэш операции синхронизации по всей шине (дорого!).
одна вещь, которая может очень легко заставить вашу программу работать на одном и том же процессоре все время,-это чрезмерное использование блокировок и общей памяти. Ваши темы не должны разговаривать друг с другом. Чем реже ваши потоки используют одни и те же объекты в одной и той же памяти, тем чаще они будут работать на разных процессорах. Чем чаще они используют одну и ту же память, тем чаще они должны блокировать ожидание другого потока.
всякий раз, когда ОС видит один блок потока для другого потока, он будет запускать этот поток на том же процессоре, когда это возможно. Это уменьшает объем памяти, которая перемещается по шине между процессорами. Это то, что я думаю, вызывает то, что вы видите в своей программе.
во-первых, я бы предложил читать "параллелизм на практике" Брайана Гетца.
Это, безусловно, лучшая книга, описывающая параллельное Программирование java.
параллелизм 'легко учиться, трудно овладеть'. Я бы посоветовал много читать об этом предмете, прежде чем пытаться. Это очень легко получить многопоточную программу для правильной работы 99,9% времени, и сбой 0,1%. Тем не менее, вот несколько советов, чтобы получить вас начал:
есть два распространенных способа заставить программу использовать более одного ядра:
- запустите программу с помощью нескольких процессов. Примером является Apache, скомпилированный с предварительной вилкой MPM, которая назначает запросы дочерним процессам. В многопроцессорной программе память по умолчанию не используется совместно. Однако, можно сопоставить разделы общей памяти между процессами. Apache делает это с его "табло".
- сделать программу многопоточной. В многопоточная программа, вся память кучи совместно используется по умолчанию. Каждый поток по-прежнему имеет свой собственный стек, но можете получить доступ к любой части кучи. Как правило, большинство Java-программ является многопоточным, а не многоступенчатый процесс.
на самом низком уровне, можно создание и уничтожение потоков. Java позволяет легко создавать потоки в портативном кросс-платформенном режиме.
поскольку он имеет тенденцию становиться дорогим для создания и уничтожения потоков все время, Java теперь включает исполнители для создания многоразовых пулов потоков. Задачи могут быть назначены исполнителям, и результат может быть получен через будущий объект.
Как правило, одна задача может быть разделена на более мелкие задачи, но конечные результаты должны быть сведены вместе. Например, с помощью сортировки слиянием можно разделить список на меньшие и меньшие части, пока не будет выполнено сортировка каждого ядра. Однако, поскольку каждый подсписок сортируется, он должен быть объединен для того, чтобы получить окончательный отсортированный список. Поскольку это проблема "разделяй и властвуй" довольно распространена, есть JSR framework который может обрабатывать базовое распределение и присоединение. Эта структура, вероятно, будет включена в Java 7.
нет никакого способа, чтобы установить соответствие процессоров в Java. http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4234402
Если вам нужно это сделать, используйте JNI для создания собственных потоков и установки их сходства.
вы должны написать программу, чтобы сделать свою работу в виде много вызываемого передается ExecutorService и выполняется с invokeAll(...).
затем вы можете выбрать подходящую реализацию во время выполнения из класса Executors. Было бы предложение вызвать исполнителей.newFixedThreadPool () с числом, примерно соответствующим количеству ядер процессора, чтобы держать занят.
самый простой, что нужно сделать-это разбить вашу программу на несколько процессов. ОС будет распределять их по ядрам.
несколько сложнее разбить вашу программу на несколько потоков и доверять JVM, чтобы правильно их распределить. Это-как правило - то, что люди делают, чтобы использовать доступное оборудование.
Edit
как многопроцессорная программа может быть "проще"? Вот шаг в трубопровод.
public class SomeStep { public static void main( String args[] ) { BufferedReader stdin= new BufferedReader( System.in ); BufferedWriter stdout= new BufferedWriter( System.out ); String line= stdin.readLine(); while( line != null ) { // process line, writing to stdout line = stdin.readLine(); } } }каждый шаг в конвейере имеет аналогичную структуру. 9 линий накладных расходов для любой обработки включены.
это не может быть от. Но это очень просто.
общая структура ваших параллельных процессов не является проблемой JVM. Это проблема ОС, поэтому используйте оболочку.
java -cp pipline.jar FirstStep | java -cp pipline.jar SomeStep | java -cp pipline.jar LastStepосталось только разработать некоторую сериализацию для ваших объектов данных в конвейере. Норматив Сериализация работает хорошо. Читайте http://java.sun.com/developer/technicalArticles/Programming/serialization/ для подсказок о том, как сериализовать. Вы можете заменить
BufferedReaderиBufferedWriterСObjectInputStreamиObjectOutputStreamдля достижения этой цели.
Я думаю, что эта проблема связана с Java Parallel Proccesing Framework (JPPF). С его помощью можно выполнять различные задания на разных процессорах.
настройка производительности JVM была упомянута ранее в почему этот код Java не использует все ядра процессора?. Обратите внимание, что это относится только к JVM, поэтому ваше приложение уже должно использовать потоки (и более или менее "правильно"):
http://ch.sun.com/sunnews/events/2009/apr/adworkshop/pdf/5-1-Java-Performance.pdf
вы можете использовать ниже API от исполнители С версией Java 8
public static ExecutorService newWorkStealingPool()создает пул рабочих потоков с использованием всех доступных процессоров в качестве целевого уровня параллелизма.
из-за механизма кражи работы, простаивающие потоки крадут задачи из очереди задач занятых потоков и общая пропускная способность увеличится.
С grepcode реализация
newWorkStealingPoolследующим образом/** * Creates a work-stealing thread pool using all * {@link Runtime#availableProcessors available processors} * as its target parallelism level. * @return the newly created thread pool * @see #newWorkStealingPool(int) * @since 1.8 */ public static ExecutorService newWorkStealingPool() { return new ForkJoinPool (Runtime.getRuntime().availableProcessors(), ForkJoinPool.defaultForkJoinWorkerThreadFactory, null, true); }
Comments