Принуждение нескольких потоков использовать несколько центральных процессоров, когда они доступны

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

я не думаю, что существует способ обойти его все же.

66
задан Raedwald 29 April 2014 в 15:11
поделиться

6 ответов

Когда я запускаю его, кажется, что он использует один процессор, пока ему не понадобится больше, чем он использует другой процессор - есть ли что-нибудь, что я можно сделать на Java, чтобы заставить разные потоки для запуска на разных ядер / процессоров?

Я интерпретирую эту часть вашего вопроса как означающую, что вы уже рассмотрели проблему обеспечения многопоточности вашего приложения. И, несмотря на это, он не сразу начинает использовать несколько ядер.

Ответ на «есть ли способ заставить ...» (AFAIK) не напрямую. Ваша JVM и / или хост-ОС решают, сколько «собственных» потоков использовать и как эти потоки сопоставляются с физическими процессорами. У вас есть несколько вариантов настройки. Например, я нашел эту страницу , где рассказывается о том, как настроить потоки Java в Solaris. И эта страница рассказывает о других вещах, которые могут замедлить работу многопоточного приложения.

29
ответ дан 24 November 2019 в 15:00
поделиться

Проще всего разбить вашу программу на несколько процессов. ОС распределяет их по ядрам.

Несколько сложнее разбить вашу программу на несколько потоков и доверить JVM правильное их распределение. Это - обычно - то, что люди делают, чтобы использовать доступное оборудование.


Править

Как программа с несколькими процессорами может быть «проще»? Вот шаг в конвейере.

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 .

1
ответ дан 24 November 2019 в 15:00
поделиться

Вы должны написать свою программу для выполнения своей работы в виде лота Callable, переданного ExecutorService и выполненного с invokeAll (...).

Затем вы можете выбрать подходящую реализацию во время выполнения из класса Executors. Было бы предложено вызвать Executors.newFixedThreadPool () с числом, примерно соответствующим количеству ядер процессора, которые должны быть заняты.

1
ответ дан 24 November 2019 в 15:00
поделиться

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

Метод первый: определить объект Runnable или Thread (который может принимать Runnable в конструкторе) и запустить его с помощью Thread.start () метод. Он будет выполняться на любом ядре, которое ему предоставляет ОС - обычно на менее загруженном.

Учебное пособие: Определение и запуск потоков

Метод второй: определить объекты, реализующие Runnable (если они не возвращаемые значения) или вызываемый (если они есть) интерфейс, который содержит ваш код обработки. Передайте их как задачи в ExecutorService из пакета java.util.concurrent. Класс java.util.concurrent.Executors имеет набор методов для создания стандартных полезных видов ExecutorServices. Ссылка на руководство по исполнителям.

По личному опыту, фиксированные и кэшированные пулы потоков Executors очень хороши, хотя вы захотите настроить количество потоков. Runtime.getRuntime (). AvailableProcessors () может использоваться во время выполнения для подсчета доступных ядер. Вам нужно будет закрыть пулы потоков, когда ваше приложение будет завершено, иначе приложение не выйдет, потому что потоки ThreadPool продолжают работать.

Получить хорошую многоядерную производительность иногда сложно и полно ошибок:

  • Диск I / O НАМНОГО замедляет при обкатке параллельно. Только один поток должен выполнять чтение / запись на диск за раз.
  • Синхронизация объектов обеспечивает безопасность многопоточных операций, но замедляет работу.
  • Если задачи слишком большие тривиально (небольшие рабочие биты, выполнить быстро) накладные расходы на управление ими в ExecutorService стоит больше, чем вы получаете выгоду от использования нескольких ядер.
  • Создание новых объектов Thread происходит медленно. ExecutorServices попытается повторно использовать существующие потоки, если это возможно.
  • Всякие сумасшедшие вещи могут произойти, когда несколько потоков работают над чем-то. Сохраняйте простоту своей системы и старайтесь сделать задачи логически разделенными и не взаимодействующими.

Еще одна проблема: контролировать работу сложно! Хорошая практика - иметь один поток-менеджер, который создает и отправляет задачи, а затем пару рабочих потоков с рабочими очередями (с использованием ExecutorService).

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


Редактировать - Пример использования 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());
        }
    }
}
56
ответ дан 24 November 2019 в 15:00
поделиться

Во-первых, я бы посоветовал прочитать «Параллелизм на практике» Брайана Гетца .

alt text

Это, безусловно, лучшая книга, описывающая параллельное программирование на Java.

Параллелизму «легко научиться, но сложно освоить». Я бы посоветовал много прочитать об этом предмете, прежде чем пытаться. Очень легко заставить многопоточную программу работать правильно в 99,9% случаев и отказывать 0,1%. Однако вот несколько советов, которые помогут вам начать работу:

Есть два распространенных способа заставить программу использовать более одного ядра:

  1. Запустить программу с использованием нескольких процессов. Примером может служить Apache, скомпилированный с Pre-Fork MPM, который назначает запросы дочерним процессам. В многопроцессорной программе память не используется по умолчанию. Однако вы можете сопоставить разделы общей памяти между процессами. Apache делает это с помощью «табло».
  2. Сделайте программу многопоточной. В многопоточной программе вся память кучи по умолчанию является общей. Каждый поток по-прежнему имеет собственный стек, но может получить доступ к любой части кучи. Обычно большинство программ Java являются многопоточными, а не многопроцессорными.

На самом низком уровне можно создавать и уничтожать потоки . Java упрощает создание потоков переносимым кроссплатформенным способом.

Поскольку создание и уничтожение потоков всегда обходится дорого, Java теперь включает исполнителей для создания повторно используемых пулов потоков. Задачи могут быть назначены исполнителям, а результат может быть получен через объект Future.

Как правило, у человека есть задача, которую можно разделить на более мелкие задачи, но конечные результаты необходимо собрать вместе. Например, с помощью сортировки слиянием можно разделить список на все меньшие и меньшие части, пока каждое ядро ​​не будет выполнять сортировку. Однако, поскольку каждый подсписок отсортирован, его необходимо объединить, чтобы получить окончательный отсортированный список. Поскольку эта проблема «разделяй и властвуй» является довольно распространенной, существует JSR-структура , которая может обрабатывать базовое распределение и объединение. Эта структура, вероятно, будет включена в Java 7.

8
ответ дан 24 November 2019 в 15:00
поделиться

В Java нет способа установить соответствие ЦП. http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4234402

Если вам нужно это сделать, используйте JNI для создания собственных потоков и установки их соответствия.

4
ответ дан 24 November 2019 в 15:00
поделиться